【问题标题】:What purpose does upvar serve?upvar 的作用是什么?
【发布时间】:2016-02-19 23:44:06
【问题描述】:

在我目前正在处理的 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,但无法理解其优点。我的意思是为什么我们不能只使用已收到的变量作为过程中的参数。谁能详细说明一下?

【问题讨论】:

  • 您可以更改它们。用于实现引用调用

标签: tcl upvar


【解决方案1】:

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

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

通常,当您将变量的 name 传递给命令时,命令可以修改该变量。这方面的经典示例是 setincr 命令,它们都将变量名称作为第一个参数。

set thisVariable $thisValue

您也可以对过程执行此操作,但是当变量是在过程调用者的上下文中定义的变量时,您需要从过程的上下文访问该变量,该变量可能是命名空间或可能是一个不同的局部变量框架。为此,我们通常使用upvar,它将一个局部变量的别名设置为另一个上下文中的变量。

例如,这里是incr的重新实现:

proc myIncr {variable {increment 1}} {
    upvar 1 $variable v
    set v [expr {$v + $increment}]
}

为什么写入局部变量v 会导致调用者上下文中的变量被更新?因为我们给它起了别名(在内部,它是通过一个指向另一个变量的存储结构的指针来设置的;一旦完成upvar,它就会非常快)。 globalvariable使用相同的底层机制;它们都归结为快速变量别名。

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

【讨论】:

    【解决方案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"
    

    【讨论】:

      【解决方案3】:

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

      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(从10),没有upvar,我们没有。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-14
        • 2012-07-18
        • 1970-01-01
        • 2012-10-21
        • 2013-09-03
        • 1970-01-01
        相关资源
        最近更新 更多