#

條件命令可以讓程式有條件的執行某些程式碼。如果你的程式裡想要有「如果某件事是真的,才執行某些事」的功能,那麼就是使用條件命令的時候了。接下來這篇文章會簡單的介紹如何使用Tcl的條件式命令,讓你的程式可以選擇性的執行某些程式。

3.1 條件命令

讓我們先來設計一個程式,看看條件命令該何時使用。假設你設計了一個計算圓面積的程式,它可以讓使用者輸入半徑,然後自動算出圓面積。這個程式執行起來像這個樣子:

圖3-1 圓面積計算程式


使用者可以在文字方塊上輸入圓的半徑,按下計算後程式會自動算出圓面積,然後顯示在同一個文字方塊上。例如:5*5*3.14=78.5

圖3-2 計算結果


它的程式碼如下:



這個程式使用::txt儲存文字方塊上顯示的文字,按下計算按鈕後會執行:



請先看到中括號裡的內容,它是我們之前提過的子命令。依照規則它會被優先執行,在中括號裡頭我們使用expr命令來計算$::txt*$::txt*3.14的值,其中::txt的內容是我們輸入的半徑,所以計算出來的結果就是圓面積。當子命令執行完成後,我們再用set命令把結果回存到::txt變數裡,這樣會讓結果顯示在文字方塊上。請注意!! 在之前的程式裡-command後面都只有放一行程式,這次我們改用多行程式的寫法,換句話說當使用者按下.btn後,夾在-command大括號裡的程式不管有多少行都會被執行。

乍看之下上面的程式好像沒什麼問題,不過實際上在某些情況下它是會運作失常的。例如,使用者故意輸入一個負數的值,或著是輸入了英文字母。想決解這些問題,可以使用條件式,因為我們要做「如果使用者輸入了正整數,才執行計算」的功能。為了單純我們先假設使用者只會輸入正數或負數而不會輸入其它的字元,那改寫後程式變成這樣:



注意到程式的第3行,我們使用了if命令限定「只有::txt的值大於0的時候,才執行第4行的程式」。這樣一來程式就不會去計算負數的輸入值了。

3.1.1 if條件命令

if是大部份程式語言都會提供的功能,它可以用條件式來限定某些程式是否要被執行。其語法如下:

if 條件式 程式區塊

在習慣上我們會用一對大括號夾起條件式及程式區塊,而且三個項目中之間一定要用空白隔開。例如:



執行結果為:

hello

上面的程式使用if命令去判斷「如果$flag等於1,才執行puts "hello"」。

如果你想要條件式成立時可以執行多行程式,可以這樣寫:



§ 特別注意

if、條件式、程式區塊,這三個項目中間最少要用1個空白隔開,否則會被當成是不合法的命令。

3.1.2 條件式

條件式基本上是由運算元及運算符號所組成,運算元可以是數值、字串、變數、命令的執行結果。運算符號則包含一般的數學上常見的加、減、乘、除...等,及邏輯運算符號。表3-1是Tcl常用的運算符號,我們可以任意的組合這些符號來使用,但前提是組合出來的條件式必需是合理的數學式,而且必須讓條件式最終的運算結果是true、false或是數值,Tcl把除了0以外的數值都當成是true而0則當成是false。

表3-1常用的運算符號

符號說明
+數學的加法。
-數學的減法。
*數學的乘法。
/數學的除法。
%取餘數。
>判斷是否大於。
>=判斷是否大於等於。
<判斷是否小於。
<=判斷是否小於等於。
==判斷是否相等。
!=判斷是否不相等。
&&邏輯的and運算。
||邏輯的or運算。

以下是一些if命令的使用例子。假設變數a的值是5,變數b的值是7。

範例條件是否為真
if {1} {puts "true"}
if {0} {puts "true"}
if {1==1} {puts "true"}
if {1>0} {puts "true"}
if {8>15} {puts "true"}
if {3*2>9} {puts "true"}
if {5%3} {puts "true"}
if {$a > 4} {puts "true"}
if {$a <=3 && $b>4 } {puts "true"}
if {$a == $b} {puts "true"}
if {$a == 5 || $b == 3} {puts "true"}

3.1.3 if命令程式實例

接下來我們要製作一個超簡單的MP3播放程式,多感受一點if命令的使用時機。這個程式的外觀很簡單只有一個選擇檔案的按鈕。如圖3-3是我們即將完成的程式:

圖3-3 簡易MP3播放器


當使用者按下選擇檔案的按鈕後,會跳出開啟檔案的對話方塊,然後使用者可以去選擇一個mp3的檔案或者是按取消(如圖3-4)。若使用者有選擇檔案,我們就假設它是一個正確的mp3檔案然後播放它,如果使用者沒有選擇檔案,那我們就不做任何事。

圖3-4 檔案選擇對話方塊


它的程式碼如下:



程式的第1行告訴Tcl要載入snack套件。snack提供了播放音樂檔案的功能,我們需要透過它來解碼MP3檔案。程式的第3~9行是建立按鈕的命令,其中4~8行是按下選擇檔案按鈕時執行的程式。第11行很單純的把.btnPlay按鈕顯示在螢幕上。

接下來請把焦點放在第4行。中括號裡的tk_getOpenFile命令會呼叫選擇檔案的對話方塊,而接在後面的-title可以用來指定它的視窗標題。當tk_getOpenFile執行後使用者可做兩種操作。一種是選中了某一個檔案然後按開啟,另一種是關閉對話方塊或按下取消鈕。如果是前者tk_getOpenFile的執行結果就是被選中檔案的完整路徑,否則的話執行結果將會是空的字串。而根據子命令的規則,不管使用者的選擇為何,其結果終將被儲存在ans變數裡。

然後請注意到第5行。我們透過if命令來判斷ans的值,如果ans不等於空字串,就表示使用者之前選中了一個檔案,那就執行第6~7行來播放音樂。其它的情況下則不做任何事。

第6行的snack::sound是snack套件提供的命令,它的功能是建立MP3播放物件,跟在後面的-file用來指定要播放的MP3檔案。我們把建立出來的播放物件儲存在player變數裡,然後在第6行使用它的play功能來播放MP3檔案。Ok!完成了。請打開喇叭然後執行這個程式。

§ 空字串

空字串指的是一對雙引號裡面沒有夾任何的字元,當然也不能包含空白鍵打出來的空白字元。

§ 關於snack套件

snack套件可以播放的格式並不只有MP3,它還可以播放許多其它的格式例如, OGG 、WAV...檔案等。另外,snack並不是Tcl內建的命令,如果你的開發環境沒有提供這個套件,那你可能沒有辦法正常的執行程式3-4。若發生這樣的狀況建議安裝ActiveTcl,然後在Windows環境下打開命令提示字元,並輸入「teacup install snack」指令來安裝,像下面的圖一樣。在Linux、Mac下請開啟終端機然後輸入一樣的命令來安裝。



3.2 二選一的條件命令 if ... else ...

下方的程式示範了一個不管條件是否成立,最少都會執行某一些程式的例子。在這個例子裡sex如果為0就會執行第二個if的命令,其它的情況下都會執行第一個if命令。換句話說這是「如果...就這樣做,否則就那樣做」的程式邏輯。 事實上用兩個if來做這種二選一的事並沒有什麼不妥,但大部份的程式語言都會提供比較漂亮的語法來專門處理這種狀況,因為二選一的情況真的太常發生了。



以下是Tcl用來處理二選一情況的命令。語法的意義是,如果條件式成立就執行程式區塊1,否則就執行程式區塊2。

if 條件式 程式區塊1 else 程式區塊2

接下來的程式和上面的功能一樣,但使用了if...else...的語法,請試著比較看看。



§ 關於if...else...

在上面的程式中2~6行可以寫成像下面的一行,但多行的排版方式會讓程式碼看起來更清礎。

if {$sex == 0} {puts "女仕您好"} else {puts "先生您好"}


3.3 多選一的條件命令 if ... elseif ... else ...

這種條件命令比較複雜一點,主要的用途是在處理多選一的情況。其觀念是讓程式設計者可以設定多組條件式及多個程式區塊,如果其中一個條件式成立時即執行對應的程式區塊,它的語法如下:

if 條件式1 程式區塊1 elseif 條件式2 程式區塊2 elseif 條件式3 程式區塊3 .... else 程式區塊X

當條件式1成立就執行程式區塊1,當條件式2成立就執行程式區塊2...依此類推,當條件式n成立就執行程式區塊n,最後的else及程式區塊X會在所有條件都不成立時被執行,若你希望所有條件都不成立時不要做任何事,可以把else及程式區塊X省略。

§ 注意

事實上你可以設定任意組的條件式及程式區塊,Tcl允許你這麼做,但請注意,不管在任何情況下,最多都只會有一個程式區塊被執行,而且是最先符合的先執行。

3.3.1 程式範例

下面的程式展示了三一律的情況:



程式輸出的結果是:

變數num的值等於5

3.4 多選一的條件命令 switch ...

switch也是一個多選一的程式結構,但它特別適合用在從多個項目中比對符合的值,然後執行對應的程式區塊, 它的語法如下:

switch ?選項? 字串 比對值1 程式區塊1 比對值2 程式區塊2 ... default 程式區塊X

用兩個問號夾起來的項目表示可以選擇性的給予。switch語法的意義是,如果字串等於比對值1就執行程式區塊1,如果字串等於比對值2就執行程式區塊2...以此類推,如果字串等於比對值n就執行程式區塊n,如果都比對失敗就執行default後面的程式區塊X,其中default及程式區塊X是可以省略的。

3.4.1 程式範例

接下來要示範一個數字轉換星期的程式,我們透過switch去比對num的值,並轉換為星期一~星期日輸出。



程式輸出的結果是:

星期三

如果對應的程式區塊裡有多行程式,下面的排版方式會比較漂亮。



本章回顧

條件命令可以限定某些程式片段符合特定的條件才執行,Tcl提供了4種條件命令,最簡單的一種如下:



夾在第2個大括號裡的程式,只有在條件式運算結果為真的時候才會執行。條件式運算的結果必需是數字、true或flase三者之一,其中除了0還有flase之外的都是真。

二選一的情況可以使用這種條件命令:



多選一則可以使用這種方式:



或是用switch:



無論是上面的哪一種條件命令,都可以在大括號裡的程式碼再出現條件命令,讓條件命令巢狀的執行。

25 個意見

嘟豆把子 | 2009年11月20日 下午1:19

請問我執行了 teacup install snack
可是卻找不到怎麼辦
Resolving snack ... Not found in the archives. Note: Names are case-sensitive.

Aborting installation, was not able to locate the requested entity.

嘟豆把子 | 2009年11月20日 下午2:08

對不起補充一下我裝的是
ActiveTcl8.5.8.0.291595-win32-ix86-threaded
的版本
麻煩您可不可以幫我解惑一下呢
謝謝

Dai | 2009年11月24日 中午12:54

套件的名字是有大小寫分別的,你可以先用下面的命令找出是否有這個套件,然後確定大小寫

teacup search snack

確定了以後再執行

teacup install XXXX

匿名 | 2010年3月8日 晚上10:57

Dai 大您好,
爬了不少您的文章, 也著時學了一些 Tcl. 有個問題想請教您. 由於我沒有 root 權限, 所以我在我的 home 下面安裝了 tcl8.5, 我到 activestate 另外下載了 snack2.2, 並另外也在程式的第一行加上了 lappend $::auto_path "$mypath".
不過由於 snack 似乎會預設去寫 /opt/dev 這個資料夾, 因為我沒有權限, 造成 snack 此套件我沒有辦法執行. 請問是否有其他解決的方法? 謝謝

不太會用 Blog 的 Hunt.

dai | 2010年3月8日 晚上11:58

在回答你的問題之前,要先請你提供一些資訊,

1.在我目前的使用狀況裡(linux、mac,winxp),都沒有遇到snack會去寫/opt/dev的例子,請問你用的作業系統是? 一般在Linux下snack會去存取/dev/dsp還有/dev/mixer,如果是這個問題就只能請你系統的管理者給予你權限了。

2.看起來你安裝的tcl可能不是ActiveTcl,因為使用ActiveTcl不用去ActiveState下載snack,請問你安裝的Tcl是由哪裡取得?

3.

匿名 | 2010年3月10日 晚上10:38

Dear Dai大,
不好意思, 公司網路都是封鎖的, 現在才有辦法回我自己的問題.
1. 是我搞錯了, 是讀取 /dev/mixer 才對, 雖然我拿到了 /dev/mixer 的寫入權限;不過 snack 也要求要 /dev/sound 的寫入權限. 所以我放棄了, 我改到Windows 上面練習.
2. 我安裝的是 ActiveTcl 沒錯, 由於我在家裡和公司的電腦都安裝了, 分成幾點撰述.
a. Windows. 我家裡的和公司的電腦都安裝了同一個 ActiveTcl 安裝檔: ActiveTcl8.5.8.1.291945-win32-ix86-threaded.exe . 不過家裡的有 snack 套件, 但是公司的沒有, 解決方法是我到 snack home 去下載並安裝. 現在兩邊都可以用了. 也許是公司的電腦我欠缺了權限? 因為公司的沒有 Admin 權限. 總之現在可以使用 snack 了.
b. Linux. 只安裝在公司的電腦, 透過 find 只找到 snack 的 License 檔, 在 lib/tcl8.5 裡面找不到 snack 套件. 用 bin/teacup install 以及 bin/teacup search 也都找不到 snack.

不太會用 Blog 的 Hunt.

dai | 2010年3月10日 晚上11:43

關於Linux下的Tcl...會不會是公司的防火牆作怪,把teacup使用到的連線或是封包之類的給擋了,或是你沒有被授權使用某些Linux上的功能 ~~ 我大概只想得到這些了 = = 嗯...祝你順利解決。

isPeter | 2010年11月8日 下午5:23

這是我的筆記,

expr 定義算式, http://wiki.tcl.tk/583

snack::sound
The Snack Sound Toolkit is designed to be used with a scripting language such as Tcl/Tk or Python.
http://www.speech.kth.se/snack/

libsnack2-alsa - Sound extension to Tcl/Tk and Python/Tkinter - Tcl/Tk library
in Debian5,apt-get install libsanck2-alsa


tk_getOpenFile
pops up a dialog box for the user to select a file to open.
http://wiki.tcl.tk/1060

dai | 2010年11月20日 上午9:48

謝謝isPeter是供的筆記

isPeter是會做筆記的乖孩子,呵~

匿名 | 2011年4月15日 上午11:09

3.3.1 程式範例
下面的程式"展範"了三一律的情況:

dai | 2011年4月16日 上午9:05

To匿名的朋友

改好了 謝謝!!

Hugo | 2011年5月31日 晚上7:00

Dai 大大你好:
真高興可以在你的網站學習Tcl,這是我第一次接觸,只是在這學習過程中有些問題,須要求救。
狀況: 在windows 7 使用命令提示字元視窗,在C:\Tcl\bin 執行 teacup install snack 可是卻找不到怎麼辦? 訊息如下─
Resolving snack ... Not found in the archives. Note that teacup's 'search' command allows you to locate packages by subject, categories, and the like.
Aborting installation, was not able to locate the requested entity.

軟體版本: ActiveTcl8.5.9.2.294317-win32-ix86_64-threaded

問題: 我家裡的的電腦安裝了ActiveTcl 安裝檔. 不過沒有 snack 套件,後來我到 snack home 去下載但沒法安裝 tclkit-win32exe . 不知道該如何使用snack來執行3-3,4的程式.

Hugo~愛吃橘子

dai | 2011年5月31日 晚上7:33

Hi Hugo,

我目前用ActiveState ActiveTcl 8.6.0.0 beta-5是沒問題的,你要不要試試改裝這個版本的Tcl?

或著是下載我平常用的snack,在這snack2.2

下載解壓縮後,把整個資料夾放在C:\Tcl\lib裡。

祝你成功~

Hugo | 2011年5月31日 晚上9:38

Dai大大 你好:
我照你解說的方式安裝8.6.0.0並且將snack2.2複製到C:\Tcl\lib後,使用ActiveTcl的Wish85 內容如下:
(System32) 1 % package require snack
couldn't load library "C:/Tcl/lib/snack2.2/libsnack.dll": invalid argument
(System32) 2 %
(System32) 2 % button .btnPlay -text "選擇檔案" -command {
> set ans [tk_getOpenFile -title "選擇MP3檔案"]
> if {$ans != ""} {
> set player [snack::sound -file $ans]
> $player play
> }
> }
.btnPlay
(System32) 3 %
(System32) 3 % pack .btnPlay
(System32) 4 %
錯誤訊息:
invalid command name "snack::sound"
invalid command name "snack::sound"
while executing
"snack::sound -file $ans"
invoked from within
".btnPlay invoke"
("uplevel" body line 1)
invoked from within
"uplevel #0 [list $w invoke]"
(procedure "tk::ButtonUp" line 24)
invoked from within
"tk::ButtonUp .btnPlay"
(command bound to event)
不知道我是否犯了甚麼錯誤動作呢? 希望可以指導我一下,謝謝。 另外我第一次使用ActiveTcl 不太曉得怎麼使用,我都是用ezdit0.92 在做練習。

Hugo | 2011年5月31日 晚上9:53

Dai大大 你好:
忘記附上在命令提示字元輸入執行指令時的狀況
C:\Tcl\bin>teacup install snack
Resolving snack ... Not found in the archives.

While a more fuzzy search disregarding letter case and accepting
substrings was done, we are sorry to say that it yielded no possible
candidates for installation either.

Questions to consider:
Have you spelled the name correctly ?
Including the proper case of characters ?

Note that teacup's 'search' command allows you to locate packages by
subject, categories, and the like.


Aborting installation, was not able to locate the requested
entity.

C:\Tcl\bin>
似乎是找不到!?

dai | 2011年5月31日 晚上10:29

hi Hugo,

唉呀~沒注意到你用的Tcl是64位元的, 我想你可能要裝32位元的Tcl哦~~這樣初期遇到的問題會比較少~

等到你Tcl玩熟了會自己編譯套件了,再選擇64位元的,可能比較好。

dai

匿名 | 2011年6月1日 晚上8:16

Hi Dai 大大你好:
感謝你的建議,使用32位元的Tcl版本確實能正常使用了。 只是用ezdit0.92就

不能包括snack進來。
另外想請教一下,你的網站上示範的程式範例圖使用的是ActiveTcl的介面擷

取圖嗎?

Hugo~~愛吃橘子

dai | 2011年6月1日 晚上10:37

hi Hugo,

恭喜你成功了,對網站上的範例都是用ActiveTcl + ezdit 寫的,所以應該不會有不能包括snack的問題 = =


dai

匿名 | 2011年6月2日 下午2:19

Hi Dai大大你好:
試了一下可以在ezdit執行了 呵呵~~
另外有兩個疑問想請教你(如果覺得很笨問題請見諒):
1. ActiveTcl的tkcon.tcl(wish.exe) 來執行程式的話 是複製貼上的部分 不能在編輯嗎? 因為我不能編輯貼上的程式碼。
2. 如果編輯途中換行按下Enter鍵,這意味著立即直譯前面編好的程式碼嗎? 想換行時只能用\代表?

Hugo~~橘子是美食
= = = = = = = = = = = = = = = = = = = =
抱怨下這個Blog都不讓我登入google帳戶來留言!

匿名 | 2012年4月14日 下午3:10

我有個疑問:TCL對於多行排版有格式上的要求嗎?
例如:if...else...的多行排版,如果把 else 單獨放在一行,程式無法順利執行

dai | 2012年4月15日 中午12:53

可以單行的像這樣:

if {$a<2} {puts "a<2"} else {puts "a>=2"}

navigationhi | 2014年7月5日 下午4:02

apt-get install libsnack2

小芽權blog | 2014年9月9日 凌晨12:02

不過選取後 指麼樣才能做撥放動作? 目前只有知道如何選擇 但選擇後 要用什麼指令?

Mike | 2014年12月23日 下午6:16

請問一下Dai大大,在tcl裡面用eval or exec處理console command,遇到錯誤的時候要如何排除問題?
例如
set file [eval exec ls abc]
puts $file
puts Next
在正常情況下有abc這個檔案,他就會把ls的訊息以及字串Next給列印出來,但若沒有abc這個檔案,
整個程序就中斷了,也不會接著把Next印出來,我甚至沒有辦法用 if 去判斷 $file 是不是空字串來
繼續我的程式,不知道這種狀況要如何處理呢?

匿名 | 2014年12月24日 下午2:49

可以用 catch 例如:

if {[catch {set file [eval exec ls abc]}] == 0} {puts $file}

留下您的意見

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates