無支撐表示式的問題
將表示式字串引數作為支撐字串提供是一種很好的做法。 雙重替代標題概述了背後的重要原因。
expr
命令評估基於運算子的表示式字串以計算值。此字串由呼叫中的引數構造。
expr 1 + 2 ; # three arguments
expr "1 + 2" ; # one argument
expr {1 + 2} ; # one argument
這三個呼叫是等效的,表示式字串是相同的。
命令 if
,for
和 while
對其條件引數使用相同的求值程式程式碼:
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]}
這個問題影響了 for
和 while
。雖然看起來這個迴圈會倒計數到 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}
幾個運算子(邏輯連線詞||
和 &&
,以及條件運算子 ?:
)被定義為不評估它們的所有運算元,但它們只能在表示式字串被支撐的情況下按設計工作。