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

Sam*_*ley 5 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.
Run Code Online (Sandbox Code Playgroud)

如果 [next] 以某种方式在同一堆栈级别运行,它是否可以在没有“uplevel”的情况下在调用者范围内创建变量?

如果不是,它就不是真正在同一级别上运行,所以它更像是一个闭包吗?

我对它与尾调用、uplevel 的使用或任何其他应考虑的概念有何不同的真实细节感兴趣。谢谢!

Don*_*ows 2

next命令在内部有点像uplevel(具体来说uplevel 1),因为它在运行超类实现时临时删除调用方法的堆栈帧,在返回时恢复堆栈帧(当然)。next next

\n

这意味着您可以重写超类中的方法,而无需那些超类为此专门做好准备。这是 Tcl 的其他一些旧对象系统的一个主要问题,您需要一个特殊的调用来获取 和 的深度参数upvaruplevel而且很容易忘记这一点,所以我对 TclOO 进行了更改。然而,这种改变的直接后果意味着你所做的事情将S1 \xc2\xbb addone不起作用;xx它在调用范围中创建/覆盖一个附加变量。S2 \xc2\xbb addone我认为这是惯用的。

\n

如果您在方法和方法之间传递内部变量,它会覆盖 \xe2\x80\x94 ,根据定义,这需要两者合作 \xe2\x80\x94 在对象的状态命名空间中使用变量;你的班级对此有完全的控制权。my或者通过or调用方法[self];这是一个标准方法调用(包含所有这些暗示)。

\n