首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我不能理解uplevel是如何工作的

我不能理解uplevel是如何工作的
EN

Stack Overflow用户
提问于 2020-03-12 20:55:05
回答 2查看 100关注 0票数 0

为什么我得到一个错误?

代码语言:javascript
复制
#!/usr/bin/tclsh
proc add {a} {
  uplevel 1 $a
   puts $a
}
set n 0
add $n

我不能理解uplevel是如何工作的

EN

回答 2

Stack Overflow用户

发布于 2020-03-12 22:08:53

uplevel在不同的“堆栈帧”中执行一些代码--每次调用过程(以及其他一些方式)时,Tcl都会向调用堆栈添加一个执行帧。

下面是一个例子:

代码语言:javascript
复制
proc foo {} {
    set fooVar 42
    bar {expr {$fooVar + 21}}
}

proc bar {code} {
    puts "in bar, code is: [list $code]"
    puts "in bar, the fooVar variable [expr {[info exists fooVar] ? "does" : "does not"}] exist"
    uplevel 1 $code
}

运行foo过程:

代码语言:javascript
复制
% foo
in bar, code is: {expr {$fooVar + 21}}
in bar, the fooVar variable does not exist
63

这演示了,因为我们在调用堆栈中的一个帧中执行代码,所以代码片段确实可以访问该帧中的局部变量。

如果我们试图用glocal作用域中的相同代码块运行bar,我们将看到一个错误:

代码语言:javascript
复制
% bar {expr {$fooVar + 21}}
in bar, code is: {expr {$fooVar + 21}}
in bar, the fooVar variable does not exist
can't read "fooVar": no such variable

但如果我们在全局作用域中设置变量,它将按预期工作:

代码语言:javascript
复制
% set fooVar -1
-1
% bar {expr {$fooVar + 21}}
in bar, code is: {expr {$fooVar + 21}}
in bar, the fooVar variable does not exist
20

结合使用uplevel命令,您可以实现自己的控制结构-- Tcl确实是一种非常灵活的语言。举个例子:

代码语言:javascript
复制
proc foreachWithIndex {variableNames aList code} {
    lassign $variableNames idxVar elemVar
    upvar 1 $idxVar idx
    upvar 1 $elemVar elem

    set idx 0
    foreach elem $aList {
        uplevel 1 $code
        incr idx
    }
}

foreachWithIndex {i e} {a b c d} {puts "$i -> $e"}
代码语言:javascript
复制
0 -> a
1 -> b
2 -> c
3 -> d
票数 0
EN

Stack Overflow用户

发布于 2020-03-13 23:21:06

如果我们运行你的代码,我们会得到错误信息:invalid command name "0"

让我们通过打印堆栈跟踪来进行研究:

代码语言:javascript
复制
% puts $errorInfo
invalid command name "0"
    while executing
"0"
    ("uplevel" body line 1)
    invoked from within
"uplevel 1 $a"
    (procedure "add" line 2)
    invoked from within
"add $n"

这看起来很奇怪,但这很好,因为$a的值是0,因为add的参数是$n的值,也就是0uplevel的参数(在您提供的可选的第一个伪数字参数之后;很好!)是一个脚本(多个参数连接在一起)。如果您的作用域中恰好有一个命令的名称是单个数字的0,那么字符串0可以是一个脚本,但这是非常不寻常的。

一旦获得要运行的脚本,uplevel 1就会在当前过程的调用者的上下文中运行脚本。(从技术上讲,它加快了1堆栈框架并在那里运行它。Tcl堆栈帧实际上形成了一棵树,尽管树上通常一次只有一个分支;uplevel形成了另一个分支。)

uplevel的两种常见形式是uplevel 1 (表示“在调用者中运行”)和uplevel #0 (表示“在全局作用域中运行”)。还有其他的,但它们并不常见。不要使用uplevel 0;它和eval完全一样,如果你这样晦涩难懂的话,混乱就会降临。

通常将您的uplevel脚本格式保持为以下格式之一是一种好的做法:

  1. 调用方传入的参数。(这是您的脚本的情况。)
  2. 常量脚本。
  3. 通过调用list生成的单个命令调用。您可以在生成列表时使用{*}expansion;这是处理值列表或命令前缀的好方法。

所有这些都被发现通常是不容易混淆的,并且相当容易正确使用(没有奇怪的危险)。其他选择往往比较棘手。

我猜你的代码应该是这样写的:

代码语言:javascript
复制
proc add {varName} {
    upvar 1 $varName v
    set v [expr {$v + 1}]
    # Or maybe: incr v
    puts $v
}
set n 0
add n

upvar命令使用与uplevel相同的级别解析规则,但将该作用域中的命名变量别名为当前作用域中的其他本地名称。对upvar #0 foo foo的调用在功能上等同于明显更简单的global foo

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60654598

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档