Tcl:K 技巧/取消共享对象也用于拥抱列表



我想做的是在列表周围添加额外的大括号:

set l [list $l]

对于类型的操作

set someVar [someFunc $someVar]

建议(https://stackoverflow.com/a/67318915/3852630、https://stackoverflow.com/a/64117854/3852630、https://wiki.tcl-lang.org/page/K)使用

set someVar [someFunc $someVar[set someVar {}]]

相反,众所周知,这可以大大提高性能。

两个问题:

  • 这也适用于list,那么我应该将其用于我的特定问题吗?
set l [list $l[set l {}]]
  • 有没有另一种有效的方法来添加额外的牙套级别?

感谢您的帮助!


编辑:更正了克里斯·海特霍夫指出的错误

这是对为什么你不需要 K 技巧的解释。

Tcl的值通常是引用计数的复合值,这些值保证序列化为字符串;这通常是隐藏的信息,但这是事情真正的工作方式。事实上,我们的一些字符串有时也有复杂的内部表示,这使得 Tcl 的字符串在大多数时候比 C、C++ 或 Java 中的字符串快得多。

要使用它,这是一个模型,您必须在其中复制一个值才能修改它。但我们实际上并没有这样做;如果您必须一直复制东西,那会很慢!相反,我们只在写入共享值时获取副本;该值是浅层重复数据删除,更新应用于新副本,然后在应该使用的地方(例如,写回变量)。这是根据需要以递归方式完成的,以便在嵌套列表或字典深处进行修改时会发生正确的事情。

何时共享价值观?嗯,有几个地方可以保存引用(例如,列表和字典),但这里的关键是:

  1. 变量。(咄!这是一个非常明显的问题!
  2. Tcl 执行堆栈。这是 Tcl 保存替换、变量读取和命令的结果的地方,直到它可以调度到下一个即将调用的命令。理论上可以使用替代执行机制,但您仍然必须将引用保留相同的时间才能获得正确的执行模型。

那么,在K的经典案例中会发生什么?好吧,你首先做set x [linsert $x 0 a](忽略字节编码)会做:

  1. set.
  2. x.
  3. linsert
  4. 推送从x读取的值。(请注意这一点:它现在有两个引用,一个来自变量,一个来自堆栈)
  5. 0
  6. a
  7. 调用 (linsert) 消耗堆栈中的 4 个值(步骤 3-6)并推送结果。只有在命令实现返回后,才会删除对参数的值引用,从而确保参数不会从命令的脚下消失。
  8. 调用(set)使用堆栈中的 3 个值(步骤 1、2 和 7)。

如果值未共享,则linsert的实现可以对列表进行就地编辑,但如步骤 4 中所述,该值自然是共享的,因此必须对其进行重复数据删除,因此这必然是一项代价高昂的操作。

经典的K技巧是这样的:

proc K {x y} {return $x};  # This is the K combinator
set x [linsert [K $x [set x DUMMY]] 0 a]

这有效地执行了从变量中获取(留下任意值)。您正在使用它的优化版本(这取决于字节码技巧和字符串连接实现中的语义优化)。

但是为什么我们不需要它set x [list $x]呢?好吧,在这种情况下,我们不更新值,我们只是将其封装在列表包装器值中,然后更新变量。值不是变量。您可以使用lindex $x 0获得以前存在的确切值;使用tcl::unsupported::representation亲自确认这一点,其输出包括值实际存储在内存中的位置(以及引用计数和类型信息)。这是一个简洁的调试工具,尽管有时令人惊讶!


[编辑]:为了说明我的意思,这里有一个小的互动环节。特别注意object pointer;这是Tcl_Obj结构的内存地址,实际上是值的真实标识。(这在Tcl中通常被严格忽略;值不应该具有这样的标识,因为这是一个为变量等命名实体保留的概念。随着被命名,事物可以修改的想法。

% set x "a b c"
a b c
% tcl::unsupported::representation $x
value is a pure string with a refcount of 4, object pointer at 0x7ff2eba275b0, string representation "a b c"
% set x [list $x]
{a b c}
% tcl::unsupported::representation $x
value is a list with a refcount of 2, object pointer at 0x7ff2eba26ec0, internal representation 0x7ff2eb02f290:0x0, string representation "{a b c}"
% tcl::unsupported::representation [lindex $x 0]
value is a pure string with a refcount of 4, object pointer at 0x7ff2eba275b0, string representation "a b c"
% set x [linsert $x 0 d]
d {a b c}
% tcl::unsupported::representation $x
value is a list with a refcount of 2, object pointer at 0x7ff2eba2b930, internal representation 0x7ff2eb02d090:0x0, string representation "d {a b c}"

如您所见,原始值仍在列表中;它没有以任何方式更改。

是的,这应该有效。list只是另一种可能的东西,可以替代K建议中的someFunc

不过,您的列表的具体示例不太正确。

set l [list $l[set $l {}]]

..您希望将空字符串分配给变量名l,而不是$l的值才能实际受益。
改为执行此操作:

set l [list $l[set l {}]]

最新更新