Tcl:K 技巧/取消共享对象也用于包含列表?

Tcl: K trick / unsharing objects also for embracing a list?

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

set l [list $l]

对于

类型的操作
set someVar [someFunc $someVar]

推荐(, , https://wiki.tcl-lang.org/page/K)使用

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

相反,它可以显着提高性能。

两个问题:

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

感谢您的帮助!


编辑:更正了 Chris Heithoff 指出的错误

是的,应该可以。 list 只是 K.

推荐中替代 someFunc 的另一个可能的东西

你的列表具体例子不太正确。

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

..你想给变量名l分配一个空字符串,而不是$l的值才真正受益。
改为这样做:

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

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

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

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

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

  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)并推送结果。对参数的值引用仅在命令执行后删除 returns,确保参数不会从命令的脚下消失。
  8. 调用(set)消耗堆栈中的 3 个值(步骤 1、2 和 7)。

如果值未共享,linsert 的实现可以对列表进行 in-place 编辑,但如第 4 步所述,值自然是共享的,因此必须删除重复数据,使得这必然是一项昂贵的操作。

经典的K招是这样的:

proc K {x y} {return $x};  # This is the K combinator

set x [linsert [K $x [set x DUMMY]] 0 a]

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

但是为什么 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}"

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