Clojure 學習筆記 :9 條件分支結構

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 中所接受的鍵值對中的 鍵,一定是直接寫出的常量,而不能是需要計算才能得出值的表達式。


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容