tcl - upvar、TclOO 和 next - (可能)意外行为的解释
问题描述
我想知道是否有人可以解释为什么我可以在嵌套过程中成功链接 upvars 的幕后细节,但它在嵌套的 TclOO 方法中不起作用(这些方法在子类中被覆盖)。(有人告诉我,在 TclOO 类方法中调用 [next] 有点像“临时尾调用”,因为没有创建新的堆栈级别。是这样吗?如果是这样,完整的图片是什么?)
例如,以下三种方法并不都给出相同的结果:
proc addone {varname} {
upvar $varname x;
incr x;
}
proc addanotherone {varname} {
upvar $varname xx;
addone xx;
incr xx;
}
oo::class create C1 {
method addone {varname} {
upvar $varname x;
incr x;
}
}
oo::class create S1 {
superclass C1;
method addone {varname} {
upvar $varname xx;
next xx;
incr xx;
}
}
oo::class create S2 {
superclass C1;
method addone {varname} {
upvar $varname xx;
next $varname;
incr xx;
}
}
set s1 [S1 new];
set s2 [S2 new];
set y 1;
addanotherone y;
set y; # First result gives 3, as expected;
set y 1;
$s1 addone y;
set y; # gives 2, unexpected;
set y 1;
$s2 addone y;
set y; #gives 3, unexpected, because original varname seems to be "two levels" deep.
如果 [next] 以某种方式在相同的堆栈级别运行,它可以在没有“uplevel”的情况下在调用者范围内创建变量吗?
如果不是,它不是真的在同一级别运行,那么它更像是一个闭包吗?
我对它与尾声、上层使用或应考虑的任何其他概念有何不同的真正细节感兴趣。谢谢!
解决方案
该next
命令在内部有点像uplevel
(特别是uplevel 1
),因为它在运行超类实现时临时删除方法调用的堆栈帧,在返回时恢复堆栈帧(当然)。next
next
这意味着您可以覆盖超类中的方法,而无需为这些超类特别准备。这是 Tcl 的一些其他旧对象系统的一个主要问题,您需要一个特殊的调用来获取upvar
and的深度参数uplevel
,而且很容易忘记这一点,所以我为 TclOO 更改了一些东西。但是,这种变化的直接后果意味着您正在做的事情S1 » addone
将行不通。xx
它在调用范围内创建/覆盖一个附加变量。S2 » addone
是我认为惯用的。
如果你在一个方法和它覆盖的方法之间传递一个内部变量——根据定义,这需要两者合作——在对象的状态命名空间中使用一个变量;你的班级可以完全控制它。my
或者通过or调用方法[self]
;这是一个标准的方法调用(所有暗示)。
推荐阅读
- bash - 带有函数的 Bash 脚本
- java - 比较来自 JDBC ResultSet 的文本输出时出错
- javascript - 不区分大小写的单词Javascript的正则表达式
- java - Android Firebase:如何从对象内部获取对象列表
- java - 制作接受姓名、时间、工资率的并行数组
- python - 函数中的 NameError (python 3.6)
- python - GroupBy 并将元素与 Python 中的组的分位数进行比较
- php - 显示 symfony 表单
- android - Android 内部共享存储中的 Xamarin SqlLite 数据库
- php - 使用 AJAX 更改面板标题文本