Tcl 快速字符串替换

Tcl 快速字符串替换,tcl,Tcl,在构建了一个可能非常大的字符串之后,我将对其中的单个字符(或者字节,如果必要的话)进行大量更改,以转换为另一个字符 实际上,我的脚本正在构建一个纵横字谜,所以字符串不会很长,但我的问题是一般性的: 我怎样才能利用我没有改变字符串(或任何更好的数据类型)长度的事实来加快速度呢 我想我要寻找的部分是一种发送指针或字符串引用的方法,或者在Tcl的情况下发送变量名 我的另一个问题是C代码内部发生了什么 这个调用会将整个字符串复制零次、一次甚至两次吗 set index [expr {$row * $wi

在构建了一个可能非常大的字符串之后,我将对其中的单个字符(或者字节,如果必要的话)进行大量更改,以转换为另一个字符

实际上,我的脚本正在构建一个纵横字谜,所以字符串不会很长,但我的问题是一般性的:

我怎样才能利用我没有改变字符串(或任何更好的数据类型)长度的事实来加快速度呢

我想我要寻找的部分是一种发送指针或字符串引用的方法,或者在Tcl的情况下发送变量名

我的另一个问题是C代码内部发生了什么

这个调用会将整个字符串复制零次、一次甚至两次吗

set index [expr {$row * $width + $col}]
set puzzle [string replace $puzzle $index $index "E"]

只要满足两个条件,
string replace
操作将执行就地更改:

  • 插入的字符串必须与删除的字符串长度相同。我想这对你来说是显而易见的
  • 字符串必须位于非共享引用中,以便其他任何内容都无法观察到正在修改的值。(这是所有Tcl引用如何工作的关键部分;共享引用不能就地修改。)
  • 如书面所述,该呼叫将被复制。基于对字符串的引用处理的简单检查,这是可以预测的;问题在于,在
    字符串替换
    完成之前(设置需要结果才能工作),字符串的旧版本仍然处于
    拼图
    中。为了解决这个问题,我们做了一件稍微奇怪的事情:

    set puzzle [string replace $puzzle[set puzzle {}] $index $index "E"]
    
    是的,这很奇怪,但它工作得很好,因为与已知的空字符串连接是一种显式优化的情况,假设您处理的是未跟踪的变量。(它将与跟踪变量一起工作,但双写是可观察的,跟踪可能会做一些棘手的事情,因此您将失去优化机会。)

    如果您正在进行大量的更改,有时会改变内容的长度,那么切换到使用列表和
    lset
    会更有效。列表上的等效操作都使用相同的通用引用和就地语义,但使用列表元素而不是字符


    拆卸 我所说的优化是在
    strcat
    操作码中,并且
    strreplace
    知道在可能的情况下执行,但您看不到字节码级别的信息;几乎所有的行动都知道这一点

    % tcl::unsupported::disassemble lambda {{puzzle index} {
        set puzzle [string replace $puzzle[set puzzle {}] $index $index "E"]
    }}
    ByteCode 0x0x7fbff6021c10, refCt 1, epoch 17, interp 0x0x7fbff481e010 (epoch 17)
      Source "\n    set puzzle [string replace $puzzle[set puzzle {}]..."
      Cmds 3, src 74, inst 18, litObjs 2, aux 0, stkDepth 4, code/src 0.00
      Proc 0x0x7fbff601cc90, refCt 1, args 2, compiled locals 2
          slot 0, scalar, arg, "puzzle"
          slot 1, scalar, arg, "index"
      Commands 3:
          1: pc 0-16, src 5-72        2: pc 0-14, src 17-71
          3: pc 2-5, src 40-52
      Command 1: "set puzzle [string replace $puzzle[set puzzle {}] $inde..."
      Command 2: "string replace $puzzle[set puzzle {}] $index $index \"E..."
        (0) loadScalar1 %v0     # var "puzzle"
      Command 3: "set puzzle {}..."
        (2) push1 0     # ""
        (4) storeScalar1 %v0    # var "puzzle"
        (6) strcat 2 
        (8) loadScalar1 %v1     # var "index"
        (10) loadScalar1 %v1    # var "index"
        (12) push1 1    # "E"
        (14) strreplace 
        (15) storeScalar1 %v0   # var "puzzle"
        (17) done 
    

    如果要对任何值进行大量修改,请使用过程、lambda或方法。局部变量比全局变量快得多。所以将变量设置为空字符串,删除对它的引用?是否有任何就地指挥的计划,或者在某些地方,这会打破Tcl的主张?无论如何,谢谢你提供的信息性答案,我喜欢这种语言。对包含字符串的变量进行操作的就地命令?没有具体的计划。也没有人反对;最难的一点是选择一个名称。显然,名称应该反映您作为程序员或脚本编写者所寻找的内容。Tcl在某些命令(或所有命令)中进行了内部优化,以避免内存重新分配,这一事实肯定不会污染文档。我(认真地)建议名称
    poke
    peek
    。当然,在
    fconfigure
    命令的控制下,这是Tcl的诸多优势之一。打开一个流到内存中,这在语言中是一种新的方式吗?