upvar 有什么作用?
What purpose does upvar serve?
在我目前使用的 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 wiki 但无法理解其中的优点。我的意思是为什么我们不能只使用已收到的变量作为过程中的参数。谁能详细说一下?
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
我们没有。
I mean why cant we just use the variables that have been received as the arguments in the procedure.
可以。它只是变得烦人。
通常,当您将变量的 name 传递给命令时,命令可以修改该变量。典型的例子是 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)来完成才能正常工作,但语义不会使任何命令变得特殊。他们全部只是简单的命令。
在我目前使用的 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 wiki 但无法理解其中的优点。我的意思是为什么我们不能只使用已收到的变量作为过程中的参数。谁能详细说一下?
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
我们没有。
I mean why cant we just use the variables that have been received as the arguments in the procedure.
可以。它只是变得烦人。
通常,当您将变量的 name 传递给命令时,命令可以修改该变量。典型的例子是 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)来完成才能正常工作,但语义不会使任何命令变得特殊。他们全部只是简单的命令。