TCL命名空间和堆栈帧之间有什么区别?

Ott*_*unt 2 callstack namespaces tcl uplevel upvar

Upvar创建指向不同堆栈帧中的变量的链接,有时称为调用堆栈不同的范围.

Upvar还用于为全局(或命名空间)变量2创建别名.但是命名空间只能由namespace eval命令创建.proc命令创建一个新的堆栈帧.

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

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

Pet*_*rin 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>
Run Code Online (Sandbox Code Playgroud)

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

使用upvar你可以让命令查看自己的堆栈框架之外的东西.例如,C可以用于为全局变量upvar #0 foo bar创建别名(bar)foo,或者使用upvar 1 baz qux(注意没有#)为B的堆栈帧中qux的变量创建别名()baz.

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

C也可以::abc::def使用命名空间变量创建别名upvar #0 ::abc::def ghi,但不要这样做,namespace upvar ::abc def ghi而是使用.

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

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

正在处理的事件调用的命令使用全局级别在调用堆栈外执行.它们无法到达活动堆栈帧内部并访问驻留在那里的变量.

一个简单的演示:

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
}
Run Code Online (Sandbox Code Playgroud)