为什么我得到一个错误?
#!/usr/bin/tclsh
proc add {a} {
uplevel 1 $a
puts $a
}
set n 0
add $n我不能理解uplevel是如何工作的
发布于 2020-03-12 22:08:53
uplevel在不同的“堆栈帧”中执行一些代码--每次调用过程(以及其他一些方式)时,Tcl都会向调用堆栈添加一个执行帧。
下面是一个例子:
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过程:
% foo
in bar, code is: {expr {$fooVar + 21}}
in bar, the fooVar variable does not exist
63这演示了,因为我们在调用堆栈中的一个帧中执行代码,所以代码片段确实可以访问该帧中的局部变量。
如果我们试图用glocal作用域中的相同代码块运行bar,我们将看到一个错误:
% 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但如果我们在全局作用域中设置变量,它将按预期工作:
% 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确实是一种非常灵活的语言。举个例子:
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"}0 -> a
1 -> b
2 -> c
3 -> d发布于 2020-03-13 23:21:06
如果我们运行你的代码,我们会得到错误信息:invalid command name "0"。
让我们通过打印堆栈跟踪来进行研究:
% 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的值,也就是0。uplevel的参数(在您提供的可选的第一个伪数字参数之后;很好!)是一个脚本(多个参数连接在一起)。如果您的作用域中恰好有一个命令的名称是单个数字的0,那么字符串0可以是一个脚本,但这是非常不寻常的。
一旦获得要运行的脚本,uplevel 1就会在当前过程的调用者的上下文中运行脚本。(从技术上讲,它加快了1堆栈框架并在那里运行它。Tcl堆栈帧实际上形成了一棵树,尽管树上通常一次只有一个分支;uplevel形成了另一个分支。)
uplevel的两种常见形式是uplevel 1 (表示“在调用者中运行”)和uplevel #0 (表示“在全局作用域中运行”)。还有其他的,但它们并不常见。不要使用uplevel 0;它和eval完全一样,如果你这样晦涩难懂的话,混乱就会降临。
通常将您的uplevel脚本格式保持为以下格式之一是一种好的做法:
list生成的单个命令调用。您可以在生成列表时使用{*}expansion;这是处理值列表或命令前缀的好方法。所有这些都被发现通常是不容易混淆的,并且相当容易正确使用(没有奇怪的危险)。其他选择往往比较棘手。
我猜你的代码应该是这样写的:
proc add {varName} {
upvar 1 $varName v
set v [expr {$v + 1}]
# Or maybe: incr v
puts $v
}
set n 0
add nupvar命令使用与uplevel相同的级别解析规则,但将该作用域中的命名变量别名为当前作用域中的其他本地名称。对upvar #0 foo foo的调用在功能上等同于明显更简单的global foo。
https://stackoverflow.com/questions/60654598
复制相似问题