【问题标题】:What is the difference between a TCL namespace and a stack frame?TCL 命名空间和堆栈框架有什么区别?
【发布时间】:2015-01-15 18:22:35
【问题描述】:

Upvar 在different stack frame 中创建指向变量的链接,有时称为call stackdifferent scope

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

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

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

【问题讨论】:

    标签: namespaces tcl callstack upvar uplevel


    【解决方案1】:

    不,不完全是。命名空间和调用框架是非常不同的概念。命名空间是名称的层次结构,可以消除同义词的歧义。您的程序中可能有三个名为 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(注意不带 #)为变量创建别名 (qux) baz 在 B 的栈帧中。

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

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

    您可以使用global foo 代替upvar #0 foo 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
    }
    

    【讨论】:

    • 只有一些命令推送堆栈帧。大家都知道的是过程调用和namespace eval。 (还有一些其他的,尤其是在 TclOO 中……)
    • 谢谢@Hoodiecrow,你填写了一些细节。许多谷歌搜索确实向我揭示了命名空间与调用框架不同。并且命名空间是可选的,也许最好在小脚本中避免(但对代码重用很有用)。每当调用过程时,也会创建调用帧。进一步澄清:我是否在我的问题中陈述了任何不真实的内容?
    • @user901750:有一些有问题的陈述。您是说堆栈帧有时称为调用堆栈。这是错误的,但我想这是一个错误? proc 命令不会创建堆栈帧(但它创建的命令在调用时会创建)。命名空间不会更改 Tcl 命名上下文,但 namespace eval 会(通过创建新的堆栈帧)。 upvaruplevel 不能像这样处理(操作)命名空间,但可以像任何其他处理变量的命令一样访问命名空间变量。
    猜你喜欢
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 2016-08-18
    • 2014-08-10
    • 2021-05-10
    • 2012-07-10
    • 1970-01-01
    • 2011-12-10
    相关资源
    最近更新 更多