2015-01-15 70 views
2

Upvar创建指向different stack frame中变量的链接,有时称为call stackdifferent scopeTCL命名空间和堆栈帧有什么区别?

Upvar还用于为全局(或名称空间)变量2创建别名。但是命名空间仅由namespace eval命令创建。一个新的堆栈框架由proc命令创建。

命名空间和调用堆栈似乎是TCL naming context可以更改的两种方式。 Upvar和Uplevel可以在名称空间和调用堆栈上工作。

我说得对吗?我还没有看到调用堆栈和命名空间之间的直接比较,因此我的问题。

回答

8

不,不是。命名空间和调用框架是非常不同的概念。命名空间是可以消除同义词歧义的名称的层次结构。程序中可能有三个名为foo的变量,但如果将它们放在不同的名称空间中,它们不会发生冲突。命名空间可以用于变量名和命令名。一旦使用namespace eval创建,名称空间的内容将始终可用,直到您致电namespace delete

调用堆栈是一系列堆栈帧。第一个堆栈帧#0始终存在。无论何时调用命令,都会创建其他堆栈框架(这主要适用于用户定义过程的命令,“内置”命令遵循其自己的规则)。命令返回时,它们会再次被销毁。所以,如果你命令A和A调用命令B和B调用命令C,你有一个调用堆栈看起来像这样:

#3 : <C's variables> 
#2 : <B's variables> 
#1 : <A's variables> 
#0 : <global and namespace variables> 

每个堆栈帧是在意义上的范围,只有变量在那里创建或导入到它可以访问,除非你使用upvar。其他一切都是隐藏的。在大多数编程语言中,可以从内部作用域自动访问外部作用域的名称,例如全局作用域。在Tcl中不是这样。

使用upvar您可以让命令查看自己堆栈框外的事情。 C可以使用upvar #0 foo bar为全局变量foo创建别名(bar),或者使用upvar 1 baz qux(不带#的注释)为B的堆栈帧中的变量baz创建别名(qux)。

uplevel命令可以沿着相同的行使用在另一个栈帧中执行脚本,包括全局脚本。在执行期间,脚本可以访问堆栈帧中的所有内容,但没有其他内容,包括从中调用的堆栈帧中的变量。

C也可以使用upvar #0 ::abc::def ghi为名称空间变量::abc::def创建别名,但不要这样做,请改用namespace upvar ::abc def ghi

而不是upvar #0 foo foo你可以使用global foo导入一个全局变量。在名称空间中定义的命令内,variable命令可以导入在同一名称空间中定义的变量。

将#upvaruplevel分为#0(全局帧)或1(呼叫者的帧)通常是有用的。使用其他帧编号很容易出错,通常表示设计不佳。调用upvar 0 foo bar在同一个堆栈帧中为变量(foo)创建一个别名(bar),这可能会非常有用。

由正在处理的事件调用的命令在调用堆栈外部使用全局级别执行。他们无法到达活动的堆栈帧并访问驻留在其中的变量。

一个简单的示范:

namespace eval ::abc { 
    variable def 42 

    proc xyz {} { 
     variable def 
    } 
} 

set foo 1138 

proc A {} { 
    B 
} 

proc B {} { 
    set baz 1337 
    C 
} 

proc C {} { 
    upvar #0 foo bar 
    puts $bar 
    upvar 1 baz qux 
    puts $qux 
    namespace upvar ::abc def ghi 
    puts $ghi 
} 
+0

只有一些命令推堆栈帧。每个人都知道的是程序调用和'命名空间评估'。 (还有其他几个,特别是TclOO ...) – 2015-01-15 19:43:06

+0

谢谢,@Hoodiecrow,你填写了一些细节。很多使用Google的人向我透露,名称空间与调用框架不同。而且这些命名空间是可选的,也许最好在小脚本中避免(但对代码重用有用)。调用过程被调用时也会创建调用框架。进一步澄清:在我的问题中,我是否说过任何不真实的东西? – 2015-01-16 00:10:42

+1

@ user901750:有一些有问题的陈述。你在说栈架有时被称为调用堆栈。这是错误的,但我认为这是一个错误? 'proc'命令不会创建栈帧(但它创建的命令在被调用时会执行)。命名空间不会改变Tcl的命名上下文,但是'namespace eval'会(通过创建一个新的堆栈帧)。 'upvar'和'uplevel'不能用于(操作)名称空间,但可以像处理变量的其他任何命令一样访问名称空间变量。 – 2015-01-16 08:50:56