iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 11
0
Software Development

擁抱 Clojure系列 第 11

[第 11 天] 擁抱 Clojure:流程控制(一)

流程控制(一)

流程控制是枝幹、河流與道路,將如同樹葉、土地與城市一樣的函式連結起來,藉由流程控制,程式可以選擇行走的方向,前進後退、左右轉或是不斷反覆。

本篇文章將介紹 Clojure 中流程控制的方法,其中出現的運算式與函式大不相同,它們只在需要的時候才會被求值,而不像函式在呼叫之前,所以參數必須完成求值。

條件式

if, if-not

首先要介紹的,也是在前面就提到過的 if 運算式。if 運算式接收三個參數,第一個參數運算式只要求值結果爲真,則會對第二個參數運算式求值,否則會對第三個運算式求值,第三個運算式可以提供也可以不提供。Clojure 中除了 nilfalse 之外都會視爲真:

(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-notif 的反面,如果 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, when-not

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"

if-let, when-let

如果你想要將測試函式的返回值記起來,以便之後使用,可以用 let 搭配 if 達到此功能:

(let [is-small (< 5 100)]
  (if is-small
    "smaller"
    "bigger"))
;; => "smaller"

Clojure 提供了簡便的寫法將兩者整合在一起,有 if-letwhen-let 兩個版本可用:

(if-let [is-smaller (< 5 100)]
  "smaller"
  "greater")
;; => "smaller"
(when-let [is-greater (> 100 5)]
  "greater")
;; => "greater"

cond

cond 運算式類似於其他語言中的 switch-caseif-elsif,它接受一對對運算式,每對運算式都有條件式,以及當條件式成立時,待求值的運算式。它會根據每對運算式,照順序對各個條件式一一測試,只要有一條件式爲真,則求值對應的運算式,就不會再往下求值:

(let [x 1]
  (cond
    (> x 0) "greater"
    (= x 0) "zero"
    (< x 0) "smaller"))
;; => "greater"

從以上範例可以看到,每個條件判斷式之後都跟着一個運算式,依序對條件判斷式求值,若爲真則對之後的運算式求值,而不再繼續。

你可以在 cond 的最後一對運算式中,將條件判斷式擺放非 nilfalse 的值,當前面的條件判斷式都失敗時,便會執行最後一段運算式,用來當作其他條件都失敗的預設值:

(let [temperature 20]
  (cond
    (> temperature 30) "Hot"
    (< temperature 15) "Cold"
    :default "Normal"))
;; => "Normal" 

case

casecond 非常類似,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

(未完待續)


上一篇
[第 10 天] 擁抱 Clojure:繫結與函式(四)
下一篇
[第 12 天] 擁抱 Clojure:流程控制(二)
系列文
擁抱 Clojure30

尚未有邦友留言

立即登入留言