iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0

[Day13] Clojure Flow Control (3) case / cond vs. condp / doall vs. dorun

早安!今天要聊的是條件判斷常用的function(case / cond / condp),以及再介紹一個迭代系列的function doall

case

先用case舉個簡單例子,在以下的myboolean為表達式;子句:true的話是1,其餘是0

(let [myboolean false]
  (case myboolean
    true 1
    0))
=> 

case 會先接表達式後接子句,這個篇幅附上文件的解說

(case expression & clauses)

Takes an expression, and a set of clauses.
Each clause can take the form of either

- test-constant result-expr
- (test-constant1 ... test-constantN)  result-expr

The test-constants are not evaluated. 

Unlike cond and condp, 
case does a constant-time dispatch, 
the clauses are not considered sequentially.  

All manner of constant expressions are acceptable in case, 
including numbers, strings, symbols, keywords, 
and (Clojure) composites thereof. 

Note that since lists are used to group multiple constants that map to the same expression, a vector can be used to match a list if needed. 

The test-constants need not be all of the same type.

上段?主要在說明casecond condp的差別,
以及Constant time dispatch (時間複雜度是O(1))

每個語言及各種function被發明時都有其特性、都有適合解決問題的情境,
也都有需要權衡好處和不好的地方

我覺得一時三刻沒辦法全部體會是很正常的,但是隨著每天去研究,
基礎知識會漸漸增加,也盡量找機會去用用看,就可以逐步理解囉:)

另外我們也知道,
在其他語言如javascript也有switch / case的用法:

來舉個例子看今天是星期幾好了!


一起來深入淺出javascript一下

  • javascript版本:
new Date()
//=> Mon Sep 27 2022 06:51:16 GMT+0800 (Taipei Standard Time)

new Date().getDay()
//=> 2 (寫文章的今天,星期二,getDay()為2)

switch (new Date().getDay()) {
  case 0:
    day = "Sunday";
    break;
  case 1:
    day = "Monday";
    break;
  case 2:
     day = "Tuesday";
    break;
  case 3:
    day = "Wednesday";
    break;
  case 4:
    day = "Thursday";
    break;
  case 5:
    day = "Friday";
    break;
  case 6:
    day = "Saturday";
}
//=> 'Tuesday'

```clojure

- clojure版本

先查一下怎麼在clojure[取得日期和時間](get date and time in Clojure?)

```clojure
(let [day (.getDay (java.util.Date.))]
  (case day
        0 "Sunday"
        1 "Monday"
        2 "Tuesday"
        3 "Wednesday"
        4 "Thursday"
        5 "Friday"
        "Saturday"))
=> Tuesday

(java.util.Date.)                                             
=> "2022-09-27T07:18:35.289-00:00"

(.getDay (java.util.Date.))
=> 2

用clojure寫除了行數變少,
語法結構是不是簡潔有力很多呢?
(少了重複的day= / break / case只要寫一次!)

cond

接下來進入重頭戲:cond
cond系列有剛剛在文件裡的描述,被拿來跟case相提並論

cond
condp

cond用法

(cond & clauses)

Takes a set of test/expr pairs. 
It evaluates each test one at a time.  

If a test returns logical true, 
cond evaluates and returns the value of the corresponding expr 
and doesn't evaluate any of the other tests or exprs. 

(cond) returns nil.

首先來用 cond 判斷一下今天要不要上班

(defn weekday-or-weekend
  "Determines whether we can enjoy holiday or go to work"
  [day]
  (cond
    (= day 0) "Sunday"
    (= day 6) "Saturday"
    :else "Weekday"))

(weekday-or-weekend (.getDay (java.util.Date.))) 
=> "Weekday"
;; 乖乖上班吧!

condp

condp 顧名思義就是在cond後再加一個 p
(predicate item,描述的item)條件邏輯為true時的判斷
而這個p第10天學filter時也有遇到。

特別的地方在於filter的條件是所有子句(clause)共用的,

condp is similar to cond except it takes a shared predicate (condition),
instead of passing test expressions, test values are passed. ref

(condp pred expr & clauses)

(defn grade [x]
(condp > x
60 "不及格,再加把勁,加油!"
80 "離目標80分再一下下"
"表現得很好!")) 
;; default argument to condp without test value

(grade 59)
=> "不及格,再加把勁,加油!"

(grade 75)
=> "離目標80分再一下下"

(grade 81)
=> "表現得很好!"

(grade 100)
=> "表現得很好!"

如果沒有提供default expression,call function時又沒有match到任何條件,
就會丟IllegalArgumentException。之後也會花個篇幅來研究一下怎麼看clojure的error msg

(defn grade [x]
(condp > x
60 "不及格,再加把勁,加油!"
80 "離目標80分再一下下")) 

(grade 100)
=> Execution error (IllegalArgumentException) at tutorial.core/grade (form-init6302935439684473751.clj:2).
No matching clause: 100

明天我們的主題預計是講 thread-first macro (->) vs thread-last macro (->>)

到時候再來聊另外兩個function cond-> cond->>

doall

Clojure Flow Control的第一篇我們講了 do,第二篇講了doseq
今天想要提一下doall

在文件分類裡,doall 跟 doseq一樣用在 Sequences Iteration

不知不覺這張表裡我們已經會了好多function啦!
鐵人賽系列目前有舉過的例子:for / doseq / map / reduce / reductions

When lazy sequences are produced via functions 
that have side effects, 

any effects other than those needed to produce the first element in the seq do not occur until the seq is consumed. 

doall can be used to force any effects. 

Walks through the successive nexts of the seq, 
retains the head and returns it, 
thus causing the entire seq to reside in memory at one time.

舉幾個分解動作的例子來了解data structure和return value:

  1. println會印出執行結果在螢幕上,但回傳是(nil nil nil)
(map #(println "good morning, " %) ["Goma" "Cara" "Sophia"])
=>
good morning,  Goma
good morning,  Cara
good morning,  Sophia

(nil nil nil)
  1. 用def裝起來
 (def print-seq  (map #(println "good morning, " %) ["Goma" "Cara" "Sophia"]))
=>
#'tutorial.core/print-seq
  1. 想要在畫面有印數字,又能retains the head and returns it
(def print-seq (doall (map #(println "good morning, " %) ["Goma" "Cara" "Sophia"])))

=>
good morning,  Goma
good morning,  Cara
good morning,  Sophia
#'tutorial.core/print-seq

dorun

dorun和doall的細微差別,在於沒有回傳值包含sequence的head (does not retain the head and returns nil.)

(dorun (map #(println "good morning, " %) ["Goma" "Cara" "Sophia"]))
 
=>
good morning,  Goma
good morning,  Cara
good morning,  Sophia
nil

道完早安,要去上班囉!:)


上一篇
[Day12] Clojure Flow Control (2) while / when / doseq
下一篇
[Day14] Clojure Macro (1) 初探 Macro
系列文
後端Developer實戰ClojureScript: Reagent與前端框架 Reframe30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言