Какой цели служит упвар?

В коде 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 мы не сделали.

Другие вопросы по тегам