为什么在TCL开关中,“$ z”与ONE不同

时间:2017-09-03 01:28:53

标签: tcl

set x "ONE"
set y 1
set z ONE
# This is probably the easiest and cleanest form of the command
# to remember:
switch $x {
    "$z" {
        set y1 [expr {$y+1}]
        puts "MATCH \$z. $y + $z is $y1"
    }
    ONE {
        set y1 [expr {$y+1}]
        puts "MATCH ONE. $y + one is $y1"
    }
    TWO {
        set y1 [expr {$y+2}]
        puts "MATCH TWO. $y + two is $y1"
    }
    THREE {
        set y1 [expr {$y+3}]
        puts "MATCH THREE. $y + three is $y1"
    }
    default {
        puts "$x is NOT A MATCH"
    }
}

switch $x "$z" {
    set y1 [expr {$y+1}]
    puts "MATCH \$z. $y + $z is $y1"
} ONE {
    set y1 [expr {$y+1}]
    puts "MATCH ONE. $y + one is $y1"
} TWO {
    set y1 [expr {$y+2}]
    puts "MATCH TWO. $y + two is $y1"
} THREE {
    set y1 [expr {$y+3}]
    puts "MATCH THREE. $y + three is $y1"
} default {
    puts "$x does not match any of these choices"
}

在第一个打印MATCH ONE. 1 + one is 2。在第二个打印MATCH $z. 1 + ONE is 2。我不明白为什么。

首先,我认为第一个"$z"位于{},所以它被解释为"$z",第二个"$z"不在{}所以它被解释为"ONE"

但我尝试在ONE的第一个开关中打印"$z",就像这样

switch $x {
    "$z" {
        set y1 [expr {$y+1}]
        puts "MATCH \$z. $y + $z is $y1"
    }
    ONE {
        puts "$z"
        set y1 [expr {$y+1}]
        puts "MATCH ONE. $y + one is $y1"
    }
    TWO {
        set y1 [expr {$y+2}]
        puts "MATCH TWO. $y + two is $y1"
    }
    THREE {
        set y1 [expr {$y+3}]
        puts "MATCH THREE. $y + three is $y1"
    }
    default {
        puts "$x is NOT A MATCH"
    }
}

我发现"$z"打印为ONE

所以"$z" "ONE"的结果与第一个ONE的结果不一样?谁能帮我回答这个问题?

2 个答案:

答案 0 :(得分:2)

如果您在list命令前加上相对容易看到发生了什么:

% list switch $x {
    "$z" {
    set y1 [expr {$y+1}]
    puts "MATCH \$z. $y + $z is $y1"
    }
    ONE {
    set y1 [expr {$y+1}]
    puts "MATCH ONE. $y + one is $y1"
    }
}

结果是:

switch ONE {
    "$z" {
    set y1 [expr {$y+1}]
    puts "MATCH \$z. $y + $z is $y1"
    }
    ONE {
    set y1 [expr {$y+1}]
    puts "MATCH ONE. $y + one is $y1"
    }
}

第二种形式:

% list switch $x "$z" {
    set y1 [expr {$y+1}]
    puts "MATCH \$z. $y + $z is $y1"
} ONE {
    set y1 [expr {$y+1}]
    puts "MATCH ONE. $y + one is $y1"
}
switch ONE ONE {
    set y1 [expr {$y+1}]
    puts "MATCH \$z. $y + $z is $y1"
} ONE {
    set y1 [expr {$y+1}]
    puts "MATCH ONE. $y + one is $y1"
}

正如您所看到的,它是阻止$z扩展到" ONE"。

的大括号。

但是,你说,当你把$z放在第二个句子中时:

switch $x {
    "$z" {
        set y1 [expr {$y+1}]
        puts "MATCH \$z. $y + $z is $y1"
    }
    ONE {
        puts "$z"
        set y1 [expr {$y+1}]
        puts "MATCH ONE. $y + one is $y1"
    }
}

它打印为" ONE"。嗯,这是因为当选择了第二个子句时,它的代码部分(仅在它已被选中之后)才会在封闭的上下文中进行评估,即与评估switch的级别相同。

如果我们进一步简化:

switch $x {$z {puts a$z} ONE {puts b$z}}

在这里,switch获得两个文字参数ONE$z {puts a$z} ONE {puts b$z}(对于后者,这是字面意思"美元符号,z,空白,开括号&#34 ; ...它只是没有特殊含义的文本。)

switch内,第二个参数具有一些含义:它现在是标签和脚本的列表(但脚本仍然只是字符串)。评估所有内容的逻辑类似于"如果ONE等于' dollar-sign + z',则评估脚本{puts a $ z}在上层;否则如果ONE等于ONE,则在上层评估脚本{puts b $ z}"。

评估上层的puts b$z打印" bONE",因为我们现在回到$z被理解为变量替换而不是字符串美元符号的上下文中+ Z

有一些方法可以处理switch标签中的变量替换,最简单的方法是使用switch命令的第二种形式而不用括号列表中的大括号。另一种方法是使用if - elseif链。

答案 1 :(得分:0)

问题在于,switch以大括号形式处理大括号部分的内容作为Tcl列表而不是根据完整的Tcl脚本规则,并且禁用$[…]替换的处理。 (当然,列表中的一些元素然后被解释为脚本,但这是解析的内层。)在第二种形式中,可以使用完整的Tcl替换,因此"$z"不会扩展为只有一美元和z,而是读取变量。

大多数情况下,switch编辑的值是常量(脚本也是如此),大支撑形式是理想的;它(在我看来)也更容易写。但是,在需要复杂替代的情况下,另一种形式更合适。这就是为什么它存在的原因。从理论上讲,您可以使用list建立一个列表并使用如下所示,但我认为这是非常难看的代码:

switch $x [list \
    "$z" {
        set y1 [expr {$y+1}]
        puts "MATCH \$z. $y + $z is $y1"
    } \
    ONE {
        set y1 [expr {$y+1}]
        puts "MATCH ONE. $y + one is $y1"
    } \
    TWO {
        set y1 [expr {$y+2}]
        puts "MATCH TWO. $y + two is $y1"
    } \
    THREE {
        set y1 [expr {$y+3}]
        puts "MATCH THREE. $y + three is $y1"
    } \
    default {
        puts "$x does not match any of these choices"
    }
]

使用具有尽可能多文字的表单有一些性能奖励。特别是,如果所有“模式”都可以在字节码编译时确定为文字(并且所有主体都是文字,并且使用完全匹配 - 使用默认样式),那么switch是转换成跳转表,一旦你得到合理数量的条款,这些都很快。对于非恒定模式(或非精确匹配),每个模式必须按顺序进行测试;没有切实可行的方法来提高我所知道的效率。