каковы практические различия между upvar и глобальными командами в tcl
Я новичок в TCL и предоставляю QA для некоторого кода, разработанного другими (на самом деле нет!). В этой конкретной программе очень много глобальных переменных, и я иногда вижу, как используется upvar, часто в сочетании с global. Я понимаю, что upvar эмулирует передачу по ссылке, но в чем будет практическая разница между двумя следующими процедурами?
set myBigFatGloblVariable "hello"
proc myFirstProc { var1 var2 } {
upvar 1 $var1 local
set local [expr $var2 * 3]
}
proc mySecondProc { var2 } {
global myBigFatGlobalVariable
set $myBigFatGlobalVariable [expr $var2 * 3]
}
myFirstProc $myBigFatGlobalVariable 3
mySecondProc 3
Мне кажется, myFirstProc был бы чище и. Я что-то упустил?
Ответов (4)4
set myBigFatGlobalVariable "hello"
proc myFirstProc { var1 var2 } {
upvar 1 $var1 local
set local [expr $var2 * 3] }
proc mySecondProc { var2 } {
global myBigFatGlobalVariable
set $myBigFatGlobalVariable [expr $var2 * 3] }
myFirstProc $myBigFatGlobalVariable 3
mySecondProc 3
Большая разница между вашими двумя процессами заключается в следующем: myFirstProc устанавливает глобальный «привет», mySecondProc устанавливает локальный «привет».
mySecondProc ссылается на глобальный myBigFat ..., чтобы получить значение "hello", но не изменяет область действия переменной "hello".
myFirstProc получает значение «hello» в качестве параметра, а затем создает связь между переменной с именем «hello» на один кадр вверх по стеку и локальной переменной «local». Установка "local" приводит к установке "hello" на один кадр вверх по стеку.
Чтобы увидеть:
myFirstProc $myBigFatGlobalVariable 3
puts $hello ;# ==> 9
unset hello
mySecondProc 3
puts $hello ;# ==> can't read "hello": no such variable
Если вы действительно хотите установить глобальное приветствие от mySecondProc, вам нужно добавить global $myBigFatGlobalVariable
Они похожи, но немного отличаются.
upvar позволяет вам получить доступ к переменным до x уровней в стеке вызовов. Они не обязательно должны быть глобальными переменными.
Вы можете использовать upvar для эмуляции global, передав upvar # 0 varName localVarName В этом случае вы получите глобальную переменную с локальным именем.
Чтобы имитировать передачу по ссылке, вы передаете имя переменной, а затем вызываете upvar для этого имени.
Если вы знаете имя переменной, вы можете использовать его как есть.
Обратите внимание на следующий код:
# here there is only 1 global variable, but we also need to access to variables defined in the calling functions
proc p3 {} {
# upvar defaults to 1, so not needed to put in here
# also notice you can call upvar on more than one variable
upvar dog myDog horse myHorse cat myCat
upvar 2 cow myCow alpha myAlpha
upvar #0 samurai mySamurai
puts "Level 1: $myDog $myHorse $myCat"
puts "Level 2: $myCow $myAlpha"
puts "Global : $mySamurai"
}
proc p2 {} {
set dog "bowow"
set horse "niegh"
set cat "meow"
p3
}
proc p1 {} {
set cow "moo"
set alpha "beta"
p2
}
set samurai "japan"
p1
Это возвращает
Level 1: bowow niegh meow
Level 2: moo beta
Global : japan
upvar - это просто способ получить переменные из стека вызовов. (вызывающие функции), включая «глобальный» стек.
Разница в том, что upvar 1 $ var local заставляет local брать свое значение из переменной, названной в $ var с уровня выше. Таким образом, в myBigFatGlobalVariable $ var необязательно определять в глобальной области видимости.
proc p1 { var1 } {
upvar 1 $var1 local1
puts $local1
}
proc p2 { } {
set local2 "local2"
p1 local2
}
set global1 "global1"
p1 global1
p2
p1 распечатает значение var1 с уровня 1 над ним в стеке вызовов. Глобальная переменная всегда определяется на верхнем уровне, поэтому upvar # 0 делает то же самое, что и global.
Ты говоришь:
В этой конкретной программе очень много глобальных переменных.
Мой опыт работы со средними и очень большими приложениями Tcl (более 20 тысяч строк!) Показывает, что использование пространств имен значительно поможет получить структуру в большом количестве глобальных переменных.
Приятная часть заключается в том, что вы можете добавлять их итеративно каждый раз, когда вы создаете новый модуль в свой код, или путем рефакторинга части вашего кода.
namespace eval module1 {
variable counter
variable name
}
namespace eval module2 {
variable n
variable names
}
Вы можете ссылаться на них через module1 :: counter (точно так же, как вы можете ссылаться на глобальную переменную как :: counter
См. Страницу пространства имен вики и страницу руководства Tcl о пространствах имен для получения дополнительной информации о пространствах имен.