Если мы запустим ваш код, мы получим сообщение об ошибке: invalid command name "0"
.
Давайте исследуем, напечатав трассировку стека:
% puts $errorInfo
invalid command name "0"
while executing
"0"
("uplevel" body line 1)
invoked from within
"uplevel 1 $a"
(procedure "add" line 2)
invoked from within
"add $n"
Это выглядит любопытно, но хорошо потому что значение $a
равно 0
, поскольку аргумент add
является значением $n
, равным 0
. Аргументы uplevel
(после необязательного первого псевдо-числового аргумента c, который предоставляет ; хорошо!) Представляют собой сценарий (с объединением нескольких аргументов). Строка 0
может быть сценарием, если в области видимости есть команда с именем di git 0
, но это довольно необычно.
Один раз у него есть скрипт для запуска, uplevel 1
запускает скрипт в контексте вызывающей стороны текущей процедуры. (Технически, он увеличивает 1
стековый фрейм и запускает его там. Фреймы Tcl-стека фактически формируют дерево, хотя обычно в дереве есть только одна ветвь за раз; uplevel
образует другую ветвь.)
Две распространенные формы uplevel
: uplevel 1
(для «запуска в вызывающей программе») и uplevel #0
(для «запуска в глобальной области»). Есть и другие, но они необычные. Не используйте uplevel 0
; это похоже на eval
и путаница царит, если вы так неясны.
Хорошей практикой обычно является сохранение ваших uplevel
форм сценариев в одном из:
- Переданный аргумент в звонящего. (Это ваш сценарий.)
- Постоянный сценарий.
- Одиночный командный вызов , генерируемый путем вызова
list
. Вы можете использовать {*}expansion
при создании списка; Это хороший способ обработки списка значений или префикса команды.
Все они, как было установлено, обычно не сбивают с толку и довольно легко использовать правильно (без каких-либо странных опасностей). Другие варианты, как правило, хитрые.
Я предполагаю, что ваш код должен иметь на самом деле написано так:
proc add {varName} {
upvar 1 $varName v
set v [expr {$v + 1}]
# Or maybe: incr v
puts $v
}
set n 0
add n
Команда upvar
использует те же правила разрешения уровня, что и uplevel
, но вместо этого псевдоним именованная переменная в этой области для другого локального имени в текущей области. Вызов upvar #0 foo foo
функционально эквивалентен, по-видимому, более простому global foo
.