Оператор 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
как ожидается, будет логическим, почему? Разве второе не является допустимым выражением? Как это исправить?
4 ответа
Брекеты, {}
и скобки, ()
, не являются взаимозаменяемыми в Tcl.
Формально фигурные скобки (за одним исключением) являются своего рода цитированием, которое указывает, что никакие дальнейшие замены не должны выполняться для содержимого. В первом случае выше это означает, что этот аргумент доставляется if
без подстановки, которая оценивает его как выражение. У подъязыка выражения есть сильно аналогичная схема интерпретации скобок к общему Tcl; они обозначают буквальное значение без дальнейших подстановок.
Напротив, круглые скобки в основном не являются специальными в Tcl. Исключение составляют имена элементов массивов (например, $foo(bar)
), в подъязыке выражений (который использует их для группировки, как в математических выражениях по всему программированию) и в подъязыке регулярных выражений (где они представляют собой другой тип группировки и несколько других вещей). Вполне законно использовать круглые скобки - сбалансированные или иные - как часть имени команды в Tcl, но ваши коллеги-программисты могут в любом случае жаловаться на вас за написание непонятного кода.
Особенности
В этом конкретном случае тестовое выражение этого if
:
if {{$number == 1 && $name == "hello"} || {$number == 0&&$name == "yes"}} {...}
разбирается на:
бла #1 LOGICAL_OR бла #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"}]} {...}
Обычно это не очень хорошая идея - лишняя масса без дополнительного усиления - но имеет смысл, когда вы пытаетесь использовать динамически сгенерированное выражение в качестве условия тестирования. Не делайте этого, если вы действительно не уверены, что вам нужно это сделать! Я серьезно. Это очень продвинутая техника, которая вам вряд ли когда-нибудь понадобится, и часто есть лучший способ сделать вашу общую цель. Если вы думаете, что вам это может понадобиться, ради бога, спросите здесь на SO, и мы постараемся найти лучший путь; почти всегда есть один доступный.
В ($number == 1)
номеру присваивается 1 и производится сравнение. Пример: 1==1
здесь вывод является логическим. Но в {$number == 1 && $name == "hello"}
$ номер не назначен из-за цветочной скобки $number
по сравнению с 1
поэтому полученный результат не является логическим.
Я думаю, что сообщение об ошибке, которое вы получаете, не означает, что $number
должен быть логическим (я получил сообщение expected boolean value but got "$number == 1 && $name == "hello""
). Это означает, что строка $number == 1 && $name == "hello"
не является логическим значением - что, безусловно, верно. Если вы используете фигурные скобки в вашем if
Выражение этих строк не оценивается, а просто интерпретируется как строка символов.
Короче: if
использует специальный "мини язык" для своего сценария условия - то же самое, что понимают expr
командование Это указано в if
страница справочника:
Команда if оценивает expr1 как выражение (так же, как expr оценивает свой аргумент).
В отличие от самого Tcl, который довольно похож на LISP и / или похож на оболочку Unix, это "expr
мини-язык "более" традиционен "в том смысле, что он похож на C.