Какой цели служит упвар?
В коде TCL, над которым я сейчас работаю, аргументы в каждой процедуре upvar
'к локальной переменной, так сказать, а затем использовать. Что-то вроде этого:
proc configure_XXXX { params_name_abc params_name_xyz} {
upvar $params_name_abc abc
upvar $params_name_xyz xyz
}
С этого момента abc и xyz будут использоваться для чего угодно. Я прочитал вики upvar TCL, но не смог понять преимущества. Я имею в виду, почему мы не можем просто использовать переменные, которые были получены в качестве аргументов в процедуре. Кто-нибудь может уточнить, пожалуйста?
3 ответа
Я имею в виду, почему мы не можем просто использовать переменные, которые были получены в качестве аргументов в процедуре.
Вы можете. Это просто раздражает.
Как правило, когда вы передаете имя переменной команде, команда может изменить эту переменную. Классическими примерами этого являются set
а также incr
команды, обе из которых принимают имя переменной в качестве первого аргумента.
set thisVariable $thisValue
Вы также можете сделать это с процедурами, но тогда вам нужно получить доступ к переменной из контекста процедуры, когда она является переменной, определенной в контексте вызывающего процедуры, которая может быть пространством имен или другим фрейм локальной переменной. Для этого мы обычно используем upvar
, который создает псевдоним из локальной переменной для переменной в другом контексте.
Например, вот переопределение incr
:
proc myIncr {variable {increment 1}} {
upvar 1 $variable v
set v [expr {$v + $increment}]
}
Почему запись в локальную переменную v
заставить переменную в контексте вызывающей стороны быть обновленной? Потому что мы дали ему псевдоним (внутренне он настроен через указатель на структуру хранения другой переменной; очень быстро, как только upvar
было сделано). Тот же основной механизм используется для global
а также variable
; все они сводятся к быстрым переменным псевдонимам.
Вы можете сделать это без, если вы используете uplevel
вместо этого, но это становится более раздражающим:
proc myIncr {variable {increment 1}} {
set v [uplevel 1 [list set $variable]]
set v [expr {$v + $increment}]
uplevel 1 [list set $variable $v]
}
Это довольно противно!
В качестве альтернативы, предположим, что мы этого не делали вообще. Затем нам нужно передать переменную по ее значению, а затем присвоить результат:
proc myIncr {v {increment 1}} {
set v [expr {$v + $increment}]
return $v
}
# Called like this
set foo [myIncr $foo]
Иногда правильная вещь, но совершенно другой способ работы!
Одним из основных принципов Tcl является то, что практически все, что вы можете сделать с помощью стандартной команды библиотеки (например, if
или же puts
или же incr
) также можно сделать с помощью команды, которую вы написали сами. Там нет ключевых слов. Естественно, могут быть некоторые проблемы с эффективностью, и некоторые команды, возможно, должны быть выполнены на другом языке, например C, чтобы работать правильно, но семантика не делает какую-либо команду особенной. Они все просто команды.
upvar
Команда позволит вам изменить переменную в блоке и сделать эту модификацию видимой из родительского блока.
Попробуй это:
# a function that will modify the variable passed
proc set_upvar { varname } {
upvar 1 $varname var
puts "var was $var\n"
set var 5
puts "var is now $var\n"
}
# a function that will use the variable but that will not change it
proc set_no_upvar { var } {
puts "var was $var\n"
set var 6
puts "var is now $var\n"
}
set foo 10
# note the lack of '$' here
set_upvar foo
puts "foo is $foo\n"
set_no_upvar $foo
puts "foo is $foo\n"
Как упоминалось в комментарии выше, он часто используется для передачи аргументов функции по ссылке (вызов по ссылке). Картинка стоит тысячи слов:
proc f1 {x} {
upvar $x value
set value 0
}
proc f2 {x} {
set x 0
}
set x 1
f1 x
puts $x
set x 1
f2 x
puts $x
приведет к:
$./call-by-ref.tcl
0
1
С upvar
мы изменили переменную x
вне функции (от 1
в 0
), без upvar
мы не сделали.