Оператор if в TCL

У меня есть вопрос о выражении if в tcl следующего кода:

if {(($number == 1)&&($name == "hello")) || (($number == 0)&&($name == "yes"))} {
    #do something here
}

Вышеприведенный код работает, но если я записал его так:

if {{$number == 1 && $name == "hello"} || {$number == 0&&$name == "yes"}} {
    #do something here
}

Он жалуется, что ожидается, что $number будет логическим, почему? Является ли второе не действительным выражением? Как его исправить?

Ответ 1

Скобки, {} и круглые скобки, (), не являются взаимозаменяемыми в Tcl.

Формально фигурные скобки (с одно исключение) вид цитирования, который указывает, что дальнейшие подстановки не должны выполняться над содержимым. В первом случае выше это означает, что этот аргумент доставляется if без подстановки, который оценивает его как выражение. Субязык выражения имеет сильно аналогичную схему интерпретации фигурных скобок для общего Tcl; они обозначают буквальное значение без дальнейших замещений для выполнения на нем.

В отличие от этого, скобки в основном не являются специальными в Tcl. Исключения составляют имена элементов массивов (например, $foo(bar)) в выражении sublanguage (который использует их для группировки, как в математических выражениях по всему программированию) и в подъязыке регулярного выражения (где они являются другим типом группировки и несколько других вещей). Полностью законно использовать круглые скобки - сбалансированные или иначе - как часть имени команды в Tcl, но у вас могут быть ваши коллеги-программисты, которые жалуются на вас, чтобы писать путающий код.

Особенности

В этом конкретном случае тестовое выражение этого if:

if {{$number == 1 && $name == "hello"} || {$number == 0&&$name == "yes"}} {...}

анализируется на:

blah#1 LOGICAL_OR blah#2

где каждый blah является литералом. К сожалению, blah#1 (который в точности равен $number == 1 && $name == "hello") не имеет логической интерпретации. (Не делает blah#2, но мы никогда не удосуживаемся считать это.) Вещи, безусловно, здесь очень не так!

Самое простое исправление заключается в том, чтобы сменить эти фиктивные фигурные скобки на скобки:

if {($number == 1 && $name == "hello") || ($number == 0&&$name == "yes")} {...}

Готов поспорить, это то, что вы изначально хотели.

Предупреждение: расширенная тема

Однако другое исправление заключается в добавлении немного дополнительного:

if {[expr {$number == 1 && $name == "hello"}] || [expr {$number == 0&&$name == "yes"}]} {...}

Это, как правило, не очень хорошая идея - лишний объем без дополнительной выгоды - но имеет смысл, когда вы пытаетесь использовать динамически сгенерированное выражение в качестве условия теста. Не делайте этого, если вы действительно не уверены, что вам нужно это сделать! Я серьезно. Это очень продвинутый метод, который вам вряд ли когда-либо понадобится, и часто есть лучший способ сделать вашу общую цель. Если вы думаете, что вам это может понадобиться, ради того, чтобы спросить здесь, как это сделать, и мы постараемся найти лучший способ; там почти всегда доступно.

Ответ 2

Я думаю, что сообщение об ошибке, которое вы получаете, не означает, что $number должно быть логическим (я получил сообщение expected boolean value but got "$number == 1 && $name == "hello""). Это означает, что строка $number == 1 && $name == "hello" не является логическим значением, что определенно верно. Если вы используете фигурные скобки в выражении if, эти строки не оцениваются, а просто интерпретируются как они - как строка символов.

Ответ 3

Короче: if использует специальный "мини-язык" для своего условия script — то же самое понимается командой expr. Это указано в if странице руководства:

Команда if оценивает выражение expr1 как выражение (таким же образом, что expr оценивает его аргумент).

В отличие от самого Tcl, который довольно похож на LISP и/или Unix shell-like, что "expr мини-язык" является более "традиционным" в том смысле, что он чувствует себя как C.

Ответ 4

В ($ number == 1) присваивается номер 1 и выполняется сравнение. Ex: 1 == 1 здесь вывод булев. но в {$ number == 1 && $name == "hello" } $number не назначается из-за цветовой скобки $number сравнивается с 1, поэтому полученный результат не является логическим