無支撐表示式的問題

將表示式字串引數作為支撐字串提供是一種很好的做法。 雙重替代標題概述了背後的重要原因。

expr 命令評估基於運算子的表示式字串以計算值。此字串由呼叫中的引數構造。

expr 1 + 2    ; # three arguments
expr "1 + 2"  ; # one argument
expr {1 + 2}  ; # one argument

這三個呼叫是等效的,表示式字串是相同的。

命令 ifforwhile 對其條件引數使用相同的求值程式程式碼:

if {$x > 0} ...
for ... {$x > 0} ... ...
while {$x > 0} ...

主要區別在於條件表示式字串必須始終是單個引數。

與 Tcl 中命令呼叫中的每個引數一樣,內容可能會也可能不會被替換,具體取決於它們如何被引用/轉義:

set a 1
set b 2
expr $a + $b   ; # expression string is {1 + 2}
expr "$a + $b" ; # expression string is {1 + 2}
expr \$a + \$b ; # expression string is {$a + $b}
expr {$a + $b} ; # expression string is {$a + $b}

第三和第四種情況存在差異,因為反斜槓/支撐會阻止替換。結果仍然相同,因為 expr 中的賦值器本身可以執行 Tcl 變數替換並將字串轉換為 {1 + 2}

set a 1
set b "+ 2"
expr $a $b   ; # expression string is {1 + 2}
expr "$a $b" ; # expression string is {1 + 2}
expr {$a $b} ; # expression string is {$a $b}: FAIL!

這裡我們遇到了 braced 引數的問題:當 expr 中的求值程式執行替換時,表示式字串已經被解析為運算子和運算元,因此求值程式看到的是一個由兩個運算元組成的字串,它們之間沒有運算子。 (錯誤訊息是“missing operator at _@_ in expression "$a _@_$b"”。)

在這種情況下,呼叫 expr 之前的變數替換可以防止錯誤。支援引數阻止了變數替換,直到表示式評估,這導致了錯誤。

這種情況可能會發生,最常見的情況是將要評估的表示式作為變數或引數傳入。在這些情況下,除了保持引數 unbraced 以允許引數賦值器解包表示式字串以傳遞給 expr 之外別無選擇。

但在大多數其他情況下,支援表達並沒有什麼壞處,實際上可以避免很多問題。一些例子:

雙重替代

set a {[exec make computer go boom]}
expr $a      ; # expression string is {[exec make computer go boom]}
expr {$a}    ; # expression string is {$a}

unbraced 表單將執行命令替換,這是一個以某種方式銷燬計算機的命令(或加密或格式化硬碟,或你有什麼)。支撐形式將執行變數替換,然後嘗試(並且失敗)來建立字串“[exec make computer go boom]”。避免災難。

無盡的迴圈

set i 10
while "$i > 0" {puts [incr i -1]}

這個問題影響了 forwhile。雖然看起來這個迴圈會倒計數到 0 並退出,但 while 的條件引數實際上總是 10>0,因為這是在 while 命令被啟用時被評估的引數。當引數被支撐時,它將作為 $i>0 傳遞給 while 命令,並且變數將在每次迭代時被替換一次。改為使用它:

while {$i > 0} {puts [incr i -1]}

總評估

set a 1
if "$a == 0 && [incr a]" {puts abc}

執行此程式碼後 a 的價值是多少?由於 && 運算子僅在左運算元為真時才計算右運算元,因此該值仍應為 1.但實際上,它是 2.這是因為在表示式字串為時,引數賦值器已經執行了所有變數和命令替換評估。改為使用它:

if {$a == 0 && [incr a]} {puts abc}

幾個運算子(邏輯連線詞||&&,以及條件運算子 ?:)被定義為評估它們的所有運算元,但它們只能在表示式字串被支撐的情況下按設計工作。