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 的一些其他旧对象系统的主要问题,您需要一个特殊的调用来获取 upvar
和 uplevel
的深度参数,而且它一直是 很容易忘记这一点,所以我为 TclOO 改变了一些东西。但是,该更改的直接后果意味着您在 S1 » addone
中所做的事情将不起作用;它 creates/overwrites 调用范围中的一个附加变量 xx
。 S2 » addone
是我认为惯用的。
如果你在一个方法和它覆盖的方法之间传递一个内部变量——根据定义,这需要两者合作——在对象的状态命名空间中使用一个变量;您的 类 可以完全控制它。或者通过 my
或 [self]
调用方法;这是一个标准的方法调用(所有这些都意味着)。
我想知道是否有人可以解释为什么我可以在嵌套过程中成功链接 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 的一些其他旧对象系统的主要问题,您需要一个特殊的调用来获取 upvar
和 uplevel
的深度参数,而且它一直是 很容易忘记这一点,所以我为 TclOO 改变了一些东西。但是,该更改的直接后果意味着您在 S1 » addone
中所做的事情将不起作用;它 creates/overwrites 调用范围中的一个附加变量 xx
。 S2 » addone
是我认为惯用的。
如果你在一个方法和它覆盖的方法之间传递一个内部变量——根据定义,这需要两者合作——在对象的状态命名空间中使用一个变量;您的 类 可以完全控制它。或者通过 my
或 [self]
调用方法;这是一个标准的方法调用(所有这些都意味着)。