Tcl 变量的双引号有什么不同吗?

Tcl 变量的双引号有什么不同吗?,tcl,Tcl,对于以下代码: set str "a bb ccc" if {[string first bb "$str"] >= 0} { puts "yes" } 我的学院说我不应该双重引用$str,因为这有性能上的差异,比如TCL在内部使用$str创建一个新对象 关于这一点,我找不到令人信服的文件。你知道这个说法是否准确吗?我认为你的同事是正确的:如果Tcl在需要一个单词的地方看到普通的$str,它会解析出“str”作为变量的名称,在适当的范围内查找,然后从该变量中提取表示其值的字符串,

对于以下代码:

set str "a bb ccc"
if {[string first bb "$str"] >= 0} {
    puts "yes"
}
我的学院说我不应该双重引用$str,因为这有性能上的差异,比如TCL在内部使用$str创建一个新对象


关于这一点,我找不到令人信服的文件。你知道这个说法是否准确吗?

我认为你的同事是正确的:如果Tcl在需要一个单词的地方看到普通的
$str
,它会解析出“str”作为变量的名称,在适当的范围内查找,然后从该变量中提取表示其值的字符串,然后要求该对象生成该值的字符串表示形式。此时,字符串表示要么已经可用并缓存(在对象中)——在您的情况下,它将可用——要么由对象透明地生成并缓存

如果将变量(
$str
)的解引用放在双引号字符串中,则Tcl如下所示:当它看到第一个
在需要一个单词的地方,它会进入一种模式,在这种模式下,它将解析以下字符,并执行变量替换和命令替换,直到它看到下一个未替换的
,在这一点上,被替换的文本自打开
以来累积。”
被认为是一个单词,它最终位于(新创建的)表示该单词值的内部对象中

如您所见,在第二种(您的)情况下,将要求保存名为“str”的变量值的原始对象获取其值,然后将使用它构造另一个值,而在第一种情况下,将立即使用第一个值

现在有一个更微妙的问题。对于它评估的脚本,Tcl只保证其解释器遵守,而不再保证;其他一切都是实现细节。这些细节可能因版本而异;例如,在Tcl 8.6中,引擎已经使用非递归求值(NRE)重新实现,虽然这些是对Tcl内部的相当彻底的更改,但您现有的脚本没有注意到

我要告诉你们的是,对隐式性能“hacks”的讨论,比如我们现在讨论的,只有在应用到特定版本的运行时时才有意义。我非常怀疑Tcl目前优化了
“$str”
,只是从
$str
重新使用对象,但理论上它最终可能会启动

您的方法的真正“问题”不是性能下降,而是您似乎在自欺欺人,这导致了风格可疑的Tcl代码。让我解释一下。与“更传统”的语言(通常受C等语言的影响)相反,Tcl没有针对字符串的特殊语法。这是因为它没有字符串文字:从文字开始的脚本中的每个值最初都是字符串。任何值的实际类型在运行时由对这些值进行操作的命令定义。为了演示,
设置x10;incr x
将字符串“10”放入名为“x”的变量,然后
incr
命令将强制该变量“x”中的值将其持有的字符串“10”转换为整数(值为10);然后,这个整数将增加1(产生11),作为副作用,使字符串表示无效。如果以后执行
put$x
,字符串表示将从整数中重新生成(生成“11”),缓存在值中,然后打印

因此,您所采用的代码风格实际上试图使Tcl代码看起来更像Python(或Perl或您以前使用的任何语言),而没有真正的价值,并且对于经验丰富的Tcl开发人员来说也显得陌生。双引号和大括号在Tcl中都用于分组,而不是分别用于生成字符串值和代码块——这些只是不同分组方式的特定用例。考虑阅读更多的背景。


更新:各种类型的分组都有很好的解释,值得整体阅读。

我认为你的同事是正确的:如果Tcl在需要单词的地方看到普通的
$str
,它会解析出“str”作为变量名,在适当的范围内查找,然后从该变量中提取表示其值的字符串,然后要求该对象生成该值的字符串表示形式。此时,字符串表示要么已经可用并缓存(在对象中)——在您的情况下,它将可用——要么由对象透明地生成并缓存

如果将变量(
$str
)的解引用放在双引号字符串中,则Tcl如下所示:当它看到第一个
在需要一个单词的地方,它会进入一种模式,在这种模式下,它将解析以下字符,并执行变量替换和命令替换,直到它看到下一个未替换的
,在这一点上,被替换的文本自打开
以来累积。”
被认为是一个单词,它最终位于(新创建的)表示该单词值的内部对象中

如您所见,在第二种(您的)情况下,将要求保存名为“str”的变量值的原始对象获取其值,然后将使用它构造另一个值,而在第一种情况下,将立即使用第一个值

现在有一个更微妙的问题。对于它所评估的脚本,Tcl只保证它的解释器遵守,而不是其他;其他一切都是实现细节。这些细节可能因版本而异;例如,在Tcl 8.6中,引擎已经使用非递归求值(NRE)重新实现,虽然这些是对Tcl内部的相当彻底的更改,但您现有的脚本没有注意到

我要告诉你们的是,我们现在讨论的隐式性能“hacks”只有sens
% tcl::unsupported::disassemble script {
set str "a bb ccc"
if {[string first bb "$str"] >= 0} {
    puts "yes"
}
}
ByteCode 0x0x78710, refCt 1, epoch 15, interp 0x0x2dc10 (epoch 15)
  Source "\nset str \"a bb ccc\"\nif {[string first bb \"$str\"] >= 0} "
  Cmds 4, src 74, inst 37, litObjs 7, aux 0, stkDepth 2, code/src 0.00
  Commands 4:
      1: pc 0-5, src 1-18        2: pc 6-35, src 20-72
      3: pc 15-20, src 25-46        4: pc 26-31, src 61-70
  Command 1: "set str \"a bb ccc\""
    (0) push1 0     # "str"
    (2) push1 1     # "a bb ccc"
    (4) storeScalarStk 
    (5) pop 
  Command 2: "if {[string first bb \"$str\"] >= 0} {\n    puts \"yes\"\n}"
    (6) startCommand +30 2  # next cmd at pc 36, 2 cmds start here
  Command 3: "string first bb \"$str\""
    (15) push1 2    # "bb"
    (17) push1 0    # "str"
    (19) loadScalarStk 
    (20) strfind 
    (21) push1 3    # "0"
    (23) ge 
    (24) jumpFalse1 +10     # pc 34
  Command 4: "puts \"yes\""
    (26) push1 4    # "puts"
    (28) push1 5    # "yes"
    (30) invokeStk1 2 
    (32) jump1 +4   # pc 36
    (34) push1 6    # ""
    (36) done