流程控制是枝幹、河流與道路,將如同樹葉、土地與城市一樣的函式連結起來,藉由流程控制,程式可以選擇行走的方向,前進後退、左右轉或是不斷反覆。
本篇文章將介紹 Clojure 中流程控制的方法,其中出現的運算式與函式大不相同,它們只在需要的時候才會被求值,而不像函式在呼叫之前,所以參數必須完成求值。
首先要介紹的,也是在前面就提到過的 if
運算式。if
運算式接收三個參數,第一個參數運算式只要求值結果爲真,則會對第二個參數運算式求值,否則會對第三個運算式求值,第三個運算式可以提供也可以不提供。Clojure 中除了 nil
與 false
之外都會視爲真:
(if 42 "answer")
;; => "answer"
(if false "answer")
;; => nil
(if "hi" "say hi" "say no")
;; => "say hi"
(if true "it's true" "it's false")
;; => "it's true"
(if false "it's true" "it's false")
;; => "it's false"
如果條件測試爲假,卻沒有提供第三個參數運算式 (else 部分),結果會是 nil
。
由於 if
運算式中的第二與第三個參數,只能允許是一個運算式,如果需要放置兩個運算式以上的話則需要將多個運算式以 do
包覆起來,將會逐個求值,結果爲 do
中最後一個運算式求值的結果:
(if true
(do
(println "Success")
"It's true")
(do
(println "Fail")
"It's false"))
;; => Success
;; => "It's true"
if-not
是 if
的反面,如果 if-not
的條件測試爲真,則會對第三個參數運算式求值,否則會對第二個參數求值:
(if-not true "it's true" "it's false")
;; => "it's false"
(if-not false "it's true" "it's false")
;; => "it's true"
when
是少了 else 部分的 if
運算式,when
接受一個測試函式,如果測試函式返回真,則會對 when
的本體運算式求值後返回其值,反之則回傳 nil
。
(when false "nothing")
;; => nil
(when true "anything")
=> "anything"
而 when-not
就是 when
的反面,如果接收的測試函式返回假,便對本體運算式求值後返回其值,反之則回傳 nil
。
(when-not (> 5 2) "Five")
;; => nil
(when-not (> 2 5) "Two")
;; => "Two"
如果你想要將測試函式的返回值記起來,以便之後使用,可以用 let
搭配 if
達到此功能:
(let [is-small (< 5 100)]
(if is-small
"smaller"
"bigger"))
;; => "smaller"
Clojure 提供了簡便的寫法將兩者整合在一起,有 if-let
與 when-let
兩個版本可用:
(if-let [is-smaller (< 5 100)]
"smaller"
"greater")
;; => "smaller"
(when-let [is-greater (> 100 5)]
"greater")
;; => "greater"
cond
運算式類似於其他語言中的 switch-case
或 if-elsif
,它接受一對對運算式,每對運算式都有條件式,以及當條件式成立時,待求值的運算式。它會根據每對運算式,照順序對各個條件式一一測試,只要有一條件式爲真,則求值對應的運算式,就不會再往下求值:
(let [x 1]
(cond
(> x 0) "greater"
(= x 0) "zero"
(< x 0) "smaller"))
;; => "greater"
從以上範例可以看到,每個條件判斷式之後都跟着一個運算式,依序對條件判斷式求值,若爲真則對之後的運算式求值,而不再繼續。
你可以在 cond
的最後一對運算式中,將條件判斷式擺放非 nil
及 false
的值,當前面的條件判斷式都失敗時,便會執行最後一段運算式,用來當作其他條件都失敗的預設值:
(let [temperature 20]
(cond
(> temperature 30) "Hot"
(< temperature 15) "Cold"
:default "Normal"))
;; => "Normal"
case
與 cond
非常類似,case
會以第一個參數,與之後成對的運算式中的第一個運算式相比較,若相等則回傳之後的運算式求值的結果;若沒有任何一個相等,將丟出 IllegalArgumentException 例外:
(let [color "red"]
(case color
"red" "Rose"
"white" "Paper"
"Blue" "Sky"))
;; => "Rose"
(let [capital "Canberra"]
(case capital
"Dublin" "Ireland"
"Cairo" "Egypt"
"Tokyo" "Japan"))
;; => IllegalArgumentException No matching clause: Canberra
(未完待續)