Clojure
零基礎
學習筆記
條件語句
分支控制
順序結構、循環結構、分支結構 --- 程序的三大結構
條件控制是一種基本的需求,我們經常能遇見需要針對不同內容進行特定處理的情況。這次我們就來看一下 Clojure 中幾種常見的條件分支。
首先看一下最基本的 if
:
=> (if true
1
0)
1
不難看出,if
接受三個參數,第一個參數是一個布爾值,如果第一個參數為 true,則 if
的值就是第二個參數的值;如果第一個參數為 false,則 if
的值就是第三個參數的值。很簡單。
不過這里特別說明一個細節,Clojure 中所有的表達式都有值。if
也不例外,它也有值。if
的值是由它第一個參數的真假所決定的,這也是它稱為條件結構的原因。這使得你可以用統一的編程思想去寫 Clojure 代碼,把表達式作為一個值,放在你需要的任何位置,繼續作為一個值來代入下一步計算。
相反的,在其它很多語言里,并不是所有的式子都有值。如果你曾學習過 C、Java 等語言,會發現 if 語句本身并沒有值,僅僅起到控制程序走向的功能。Clojure 中的 if
反而更像 C、Java 里的三元運算式。
我們試著寫一個函數來做這樣的事情:傳入一個整數,打印這個數字是奇數還是偶數。
=> (defn odd-or-even
[num]
(if (= (mod num 2) 0)
(println num "是偶數")
(println num "是奇數")))
#'user/odd-or-even
=> (odd-or-even 1024)
1024 是偶數
nil
這里可以觀察到,由于 (= (mod num 2) 0)
的值為 true,所以執行了 (println num "是偶數")
,但是 println
的值為 nil,所以 if
的值也就變成了 nil。
這里有個小問題,如果傳入的是一個小數,我們的程序會錯誤的認為它是一個奇數。
Clojure 本身提供了一個返回布爾型的函數 even?
用來判斷一個數字是不是偶數,如果是偶數則返回 true,不是偶數則返回 false。所以我們也可以這么寫:
(defn odd-or-even
[num]
(if (even? num)
(println num "是偶數")
(println num "是奇數")))
這樣如果你傳入一個小數,它就會報錯提示你 “參數必須是整數”。
Clojure 還提供了一個 if-not
函數,用來做和 if
完全相反的事情。
如果遇見更為復雜的問題,比如判斷考試成績的優良中差,當然,你可以通過嵌套多層 if
來處理。不過 Clojure 提供了另外一個方便多層次判斷的東西 --- cond
。
我們來看看如何使用 cond
來解決判斷成績優良中差的問題。
=> (defn rating
[score]
(cond
(>= score 90) "優秀",
(>= score 80) "良好",
(>= score 70) "中等",
(>= score 60) "及格",
(< score 60) "較差",))
#'user/rating
=> (println (rating 78))
中等
nil
由上面的例子我們可以總結出 cond
的用法,它接受若干對參數,每一對參數分別是布爾型的表達式和另一個表達式。cond
會由上至下的判斷每個布爾表達式,一旦判斷為 true,則 cond
的值就會變成判斷為 true 后面所跟表達式的值。如果判斷到末尾依然沒有任何表達為 true,則 cond
的值等于 nil 。
注意,一旦 cond
發現 true,會停止繼續解析下面的式子,所以如果式子的先后順序出現問題就會出現意外:
=> (defn rating
[score]
(cond
(>= score 60) "及格",
(>= score 90) "優秀",
(>= score 80) "良好",
(>= score 70) "中等",
(< score 60) "較差",))
#'user/rating
=> (println (rating 98))
及格
nil
你還可以提供一個 :default
值來確保沒有任何匹配時,返回你所設置的默認值:
=> (defn rating
[score]
(cond
(>= score 90) "優秀",
(>= score 80) "良好",
(>= score 70) "中等",
(>= score 60) "及格",
:default "較差",))
#'user/rating
=> (println (rating -10))
較差
nil
有些時候我們需要處理一些固定有限的情況,比如把阿拉伯數字轉換成中文大寫數字,處理一些狀態碼,等。
這個時候 case
是最佳選擇。(相當于 C / Java 等語言中的 switch
)
=> (defn arabic-num-to-chinese-num
[arabic-num]
(case arabic-num
1 "壹",
2 "貳",
3 "叁",
4 "肆",
5 "伍",
6 "陸",
7 "柒",
8 "捌",
9 "玖",
0 "零",
"未知"))
=> #'user/arabic-num-to-chinese-num
=> (println (arabic-num-to-chinese-num 8))
捌
nil
case
的用法也很簡單直觀,它的第一個參數是一個表達式,余下參數是一些鍵值對。在這些鍵值對中,首先尋找第一個參數的值所對應的鍵,case
的值就等于我們找到的鍵所對應的值。需要注意,如果出現找不到匹配的情況,case
不會返回 nil,而是會報錯。所以我們需要在最后一行加上一個默認值,就如同上面例子中的 "未知"
。一旦出現無法匹配的情況,case
的值就是我們所設定的默認值。
需要特別注意的是,case
中所接受的鍵值對中的 鍵,一定是直接寫出的常量,而不能是需要計算才能得出值的表達式。