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

upvar, TclOO, and next - explanation of (perhaps) unexpected behaviour

我想知道是否有人可以解释为什么我可以在嵌套过程中成功链接 upvars 的幕后细节,但它在嵌套的 TclOO 方法中不起作用(那些方法在子 class)。 (有人告诉我,在 TclOO class 方法中调用 [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”的变量?

如果不是,那真的不是运行一个级别的,那么是不是更像是一个闭包?

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

next命令在内部有点像uplevel(特别是uplevel 1)因为它临时删除堆栈帧方法调用next而运行超类实现,恢复堆栈frame when next returns (当然).

这意味着您可以覆盖来自 super类 的方法,而不需要那些 super类 需要特别准备的方法。这是 Tcl 的一些其他旧对象系统的主要问题,您需要一个特殊的调用来获取 upvaruplevel 的深度参数,而且它一直是 很容易忘记这一点,所以我为 TclOO 改变了一些东西。但是,该更改的直接后果意味着您在 S1 » addone 中所做的事情将不起作用;它 creates/overwrites 调用范围中的一个附加变量 xxS2 » addone 是我认为惯用的。

如果你在一个方法和它覆盖的方法之间传递一个内部变量——根据定义,这需要两者合作——在对象的状态命名空间中使用一个变量;您的 类 可以完全控制它。或者通过 my[self] 调用方法;这是一个标准的方法调用(所有这些都意味着)。