3

在我目前处理的 TCL 代码中,每个过程中的参数都被upvar'ed 到一个局部变量,可以这么说,然后使用。像这样的东西:

proc configure_XXXX { params_name_abc params_name_xyz} {
    upvar $params_name_abc abc
    upvar $params_name_xyz xyz
}

从这里开始,abcxyz将被用来做任何事情。我阅读了upvar TCL wiki,但无法理解其优势。我的意思是为什么我们不能只使用已收到的变量作为过程中的参数。有人可以详细说明吗?

4

3 回答 3

6

我的意思是为什么我们不能只使用已收到的变量作为过程中的参数。

你可以。它只是变得烦人。

通常,当您将变量的名称传递给命令时,命令可以修改该变量。典型的例子是setandincr命令,这两个命令都将变量的名称作为它们的第一个参数。

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 的核心原则之一是,您可以使用标准库命令(例如iforputsincr)执行的几乎所有操作也可以使用您自己编写的命令来完成。没有关键字。自然可能存在一些效率问题,并且某些命令可能需要用另一种语言(例如 C)完成才能正常工作,但语义不会使任何命令特别。它们只是简单的命令。

于 2016-02-19T15:44:06.677 回答
2

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"
于 2016-02-19T14:09:25.803 回答
1

正如上面评论中提到的,它通常用于通过引用传递函数参数(通过引用调用)。一张图片需要一千个字:

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我们改变了函数之外的变量(x1to 0),没有upvar我们没有。

于 2016-02-19T14:47:52.767 回答