tcl - upvar、TclOO 和 next -(可能)意外行为的解释

标签 tcl

我想知道是否有人可以解释为什么我可以在嵌套的 proc 中成功链接 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”的情况下在调用者范围内创建变量?
如果不是,它就不是真正在同一级别上运行,所以它更像是一个闭包吗?
我对它与尾调用、uplevel 的使用或任何其他应考虑的概念有何不同的真实细节感兴趣。
谢谢!

最佳答案

next命令内部有点像 uplevel (特别是 uplevel 1 )因为它 临时移除栈帧 方法调用 next 在运行父类(super class)实现时, next 时恢复堆栈帧返回(当然)。
这意味着您可以覆盖父类(super class)中的方法,而无需为这些父类(super class)特别准备。这是 Tcl 的其他一些旧对象系统的一个主要问题,您需要一个特殊调用来获取 upvar 的深度参数。和 uplevel ,而且很容易忘记这一点,所以我为 TclOO 改变了一些东西。但是,该更改的直接后果意味着您在 S1 » addone 中所做的事情不会工作;它创建/覆盖一个额外的变量,xx , 在调用范围内。 S2 » addone是我认为惯用的。
如果你在一个方法和它覆盖的方法之间传递一个内部变量——根据定义,这需要两者合作——使用对象状态命名空间中的变量;您的类(class)可以完全控制它。或者通过 my 调用一个方法或 [self] ;这是一个标准的方法调用(包括所有这些)。

关于tcl - upvar、TclOO 和 next -(可能)意外行为的解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66201923/

相关文章:

shell - 如何一起使用 expect 和 shell 脚本

sockets - 使用TCL socket 时悬窗

linux - 期望登录后运行 shell 命令

sqlite - 如何配置sqlite3的tcltests?

tcl - 限制Tk entry widget中的输入字符数

c - 了解 TCL C API 中的结构

tcl - 将键值列表分配给 Tcl 中的数组

list - 使用 TCL 或 sqlite 从带有符号的列表中获取最高的 x 值(与符号无关)

user-interface - 主要使用 Tcl/Tk 编写的应用程序的 GUI 测试

windows - 当窗口改变位置时如何执行特定代码?