Tcl parser 處理的特殊字元


相較於其他語言, Tcl 的基本語法規則非常簡單, parser 只認得幾個特殊字元.
  1. 參數分隔字元: (空格)
  2. 命令分隔字元: ;
    可用以將數個命令放在同一列, 例如: puts hello ; puts world
  3. 註解字元: (放在一個命令最前面的) #
    注意: 如何在同一列的左邊寫命令, 右邊寫註解?
    set msg "hello, world!" ; # this is a comment
  4. 變數代換字元: $
    1. 為什麼在 簡介 單元的範例中, set 後面的 msg 不要加 $, 而 puts 和 format 後面的 msg 要加 $ ? 因為 puts 和 format 對變數的名字沒有特別的興趣, 把所有參數當做字串看; 而 set 則把參數當做變數的名字看. 究竟何時要把參數當做一般字串, 何時要把參數當做變數的名字, 由各個命令自行決定. 大部分命令對變數的名字沒有特別的興趣, 他們之所以看起來像是可以處理變數, 完全是因為 parser 事先將 $... 代換過了.
    2. 刪除變數: unset msg val
      (當然, unset 對變數的名字有興趣, 所以不要加 $)
  5. 陣列變數代換字元: $...(...)
    1. 例: set days(jan) 31 ; set days(feb) 28 ; set days(mar) 31
      之後可以這樣用: format "There are %d days in Februrary" $days(feb)
    2. 陣列的 "註標" 可以是任何字串, 這在其他語言中叫做 associative array, hash, map, 或是 dictionary.
    3. 注意: $days(jan), $days(Jan), $days( jan) 是 days 陣列中三個不一樣的元素. 同樣地, $dm(1), $dm(01), $dm( 1) 則是 dm 陣列中三個不一樣的元素.
    4. 所以 Tcl/Tk 中雖然沒有多維陣列, 我們還是可以用 "有規律的註標名稱" 來達到相同的效果: set data(3,7) hello
      在這裡 3,7 整個被視為一個字串.
    5. 看看一個陣列內有那些 "註標" 有定義: array names days
      注意: array names ... 傳回的註標順序沒有什麼章法.
    6. 刪除陣列當中的一個元素: unset days(mar)
      刪除整個陣列: unset days
  6. 命令結果代換 (command substitution): [ ... ]
    1. 題外話: 用 llength "how many words are there?" 可以計算字串內有幾個 "字" (即字串被空格分割成幾段)
    2. 假設 days 陣列內 jan, feb, mar 等三個註標對應的元素有定義, 則 程式設計師下 llength [array names days] 時, parser 會先處理 [...], 最後 llength 這個命令接手時, 看到的命令是 llength jan feb mar
  7. 簡單的 Tcl parser: 內定不代換 ("Quoting is the default!")
    1. 除了這些 (還有其他較少用的) 具有特殊意義的代換字元之外, 所有東西一律是字串, 像是 + - * / < > ... 等等.
    2. 那麼數學運算式與邏輯運算式怎麼辦呢? 用 expr 命令. expr 命令把它所有的參數串成一個很長的字串, 然後當成一個數學/邏輯運算式來計算. (所以傳給 expr 的參數有沒有空格無所謂)
  8. 避免代換 (quoting)
    1. \ 取消單一字元的特殊意義.
      例: puts \$23.5
    2. "..." 取消空格的意義.
      例: puts "hello" 和 puts hello 效果完全一樣;
      但 puts "hello world!" 的雙引號則不可省略.
    3. { ... } 取消空格的意義, 也取消所有代換. 試試看以下三例:
                  set x [expr 3 + 5]
                  set x "I have [expr 3 + 5] dollars. You have $x dollars."
                  set x {I have [expr 3 + 5] dollars. You have $x dollars.}
              
      

    Q: {hello} 和 "hello" 有什麼不同? 提示: 查看手冊 string(n), 用 string compare 比較字串. (在 UNIX 命令列下打 man n string) Q: {puts} hello 和 puts {hello} 有什麼不同?
  9. 用 quoting 把程式 "冷凍" 成資料; 用 eval 命令 "解凍" 以執行程式:
            set x [expr 3 + 5]
            set x {expr 3 + 5}
            eval $x
        
    

    C 語言高手: qsort(3) 的 "把函數當做參數傳遞" 介面, 在 tcl 下應該更簡單吧?
  10. 命令的傳回值:
    只有少數命令是用來印資料的, 例如 puts; 但是大部分命令都有傳回值, 例如 set, llength, format, ... Tcl interpreter 在處理完一整列的命令後, 會把最後一個命令的傳回值印出來. puts 只印資料, 沒有傳回值; 而 format 只有傳回值, 不印資料. 所以在程式裡面, 要把 format 出來的結果印出來, 必須和 puts 配合使用.
            set x [puts hello]
            puts $x
            format "%x" 255; format "%x" 38; format "%x" 47
            puts [format "%d decimal is %x hex" 255 255]
        
    

  11. 特殊字元的其他功能
    1. 輸入鍵盤無法敲入的字元: \
      例如 \a 為 "嗶聲" (0x7), \n 為換列 (0xa), \xhh 為 "ASCII 碼是 0xhh 的字元".
    2. 把變數的值嵌在字串當中: ...${...}...
      例如 set middle "ma"; puts infor${middle}tion (這個功能可以用來動態產生變數名稱)
    3. 奇怪的變數名稱 (建議不要這樣做!): ${...}
      例如 set 名字 李大明; info vars; puts ${名字}
      又如 set "max speed" 120; info vars; puts ${max speed}