1

我对 TCL 相当陌生,并且正在为其他人开发的一些代码提供 QA(不是真的!)。在这个特定的程序中有很多很多全局变量,我有时会看到 upvar 被使用,通常与 global 结合使用。我知道 upvar 模拟传递引用,但是以下两个 procs 之间的实际区别是什么?

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 回答 4

4

它们相似但有细微的不同。

upvar 允许您访问调用堆栈中向上 x 级的变量。它们不一定需要是全局变量。

您可以使用 upvar 通过传递 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 只是一种从调用堆栈中获取变量的方法。(调用函数)包括“全局”堆栈。

于 2009-02-27T20:49:06.003 回答
2
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

您的两个 proc 之间的最大区别在于:myFirstProc 设置全局“hello”,mySecondProc 设置本地“hello”。

mySecondProc 引用全局 myBigFat... 以获取值“hello”,但不会更改“hello”变量的范围。

myFirstProc 接收值“hello”作为参数,然后在堆栈上一帧名为“hello”的变量和局部变量“local”之间创建链接。设置“本地”具有在堆栈中设置“hello”一帧的效果。

查看:


myFirstProc $myBigFatGlobalVariable 3
puts $hello ;# ==> 9
unset hello
mySecondProc 3
puts $hello ;# ==> can't read "hello": no such variable

如果你真的想从 mySecondProc 设置全局“hello”,你需要添加global $myBigFatGlobalVariable

于 2009-03-09T11:30:10.007 回答
1

不同之处在于 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 将在调用堆栈中从它上面的级别 1 打印出 var1 的值。全局始终在顶层定义,因此 upvar #0 与全局执行相同的操作。

于 2009-02-27T20:36:38.137 回答
0

你是说:

在这个特定的程序中有很多很多的全局变量

我对中型到超大型 Tcl 应用程序(20k+ 行!)的经验是,使用命名空间将极大地帮助在大量全局变量中获取结构。

好的部分是,您可以在每次为代码创建新模块或重构某些代码时迭代地添加它们。

namespace eval module1 {
  variable counter
  variable name
}

namespace eval module2 {
  variable n
  variable names
}

您可以通过 module1::counter 引用它们(就像您可以将全局变量称为 ::counter

有关名称空间的更多信息,请参阅wiki 名称空间页面和有关名称空间的Tcl 手册页。

于 2009-03-11T06:49:52.503 回答