週日偷懶床上打滾中~
流程控制(Flow control)是每個程式語言都會有的設計,有另一個專有名詞叫做 控制結構(Control Structure)
通常在一段程式碼內的執行順序會有三種執行的方式,
循序(sequence)
: 一行一行照順序讀 (read line by line from top to bottom and from left to right)選擇(selection)
: 根據所選擇符合條件的邏輯,跳到對應適當的程式碼區塊(jump to a different part of the program)重複(repetition)
: 重複某個區塊的程式碼 re-run a certain piece code again前一週的鐵人賽所舉的例子蠻多都是屬於循序(sequence),
因此今天開始也要舉點選擇(selection) 和重複(repetition)的例子,
這樣我們未來寫的clojure程式就可以提高複雜度,從基礎到專業囉~
在第二天鐵人賽文章就有提到最基本的if else
(if boolean-form
then-form
optional-else-form)
Example: if搭配nil?
nil?
若x結果為nil,會回傳true
用if搭配nil?
簡單舉幾個例子:
(if (nil? nil) :true :false)
=> :true
空的
(if (nil? []) :true :false)
=> :false
(if (nil? [nil]) :true :false)
=> :false
(if (nil? {}) :true :false)
=> :false
當然以上的if只是為了舉超簡單例給初踏進clojure世界看倌們 XD
在前兩天介紹reduce
及filter
練習及延伸思考,
開始有了怎麼簡化語法的想法之後,
發現以上的例子全部都可以再精簡,
如果只是為了判斷,只要有最小的執行單位list就好,連if都不用啦!
(nil? nil)
=> true
(nil? [])
=> false
(nil? [nil])
false
(nil? {})
=> false
if-not 跟if相反,可以想成其他語言如ruby的unless
Collections裡面的API拿來試用看看:
Example: if-not來搭配 collections function的empty? / every?
empty?
空值為true
(empty? [])
=> true
反例的話,非空值就是false啦~
(if-not (empty? []) :true :false)
=> :false
如果不想一個一個寫,乾脆一次判斷多一點東東:
查到了every?
會檢查是否每個collection的元素都符合條件
例如每個collection member都在set裡
(every? #{"a" "b" "c"} ["a" "b"])
=> true
那我們來用every?
把collection裡的element都用 empty?
來判斷
(every? empty? [ nil "" [] () '() {} #{}])
=> true
(if-not (every? empty? [ nil "" [] () '() {} #{}]) :true :false)
=> :false
上面?every?的例子聞起來似乎有那麼一點迭代的味道...
我們接下來這裡來介紹flow control裡超級常用的 for
(for seq-exprs body-expr)
Takes a vector of one or more binding-form/collection-expr pairs, each followed by zero or more modifiers, and yields a lazy sequence of evaluations of expr.
Collections are iterated in a nested fashion, rightmost fastest, and nested coll-exprs can refer to bindings created in prior binding-forms.
文件裡有講到一個重點 lazy sequence
,在這裡先用for
舉個簡單的例子,讓range裡的每個值都可以執行list form裡的運算:
(for [x (range 1 6)]
(* x x))
(1 4 9 16 25 36)
再舉個超展開
例子:
let
是Clojure的special form,可以透過裝在vector裡建立語意化的bindings(綁定),
(for [x (range 1 6)
:let [square-x (* x x)
cube-x (* x x x)
x-to-power-of-four(* x x x x)]]
[x square-x cube-x x-to-power-of-four])
=> ([1 1 1 1] [2 4 8 16] [3 9 27 81] [4 16 64 256] [5 25 125 625])
以上回傳的是就是傳說中的LazySequence,希望之後幾天的鐵人賽有機會花篇幅來補充!
tutorial.core=> (class(for [x (range 1 6)
#_=> :let [square-x (* x x)
#_=> cube-x (* x x x)
#_=> x-to-power-of-four(* x x x x)]]
#_=> [x square-x cube-x x-to-power-of-four]))
clojure.lang.LazySeq
在vector裡第一、第二、第三、第四個元素,
每個元素分別代表著不同數字執行2次方到4次方的結果,
是不是很方便呢?
由於文件裡有提到:
Supported modifiers are:
:let [binding-form expr ...],
:while test, :when test.
看起來for當然還能跟不同條件結合!
今天週日先簡單開頭暖身一下flow control,
明天再來花先篇幅介紹 while / when / cond 以及 doseq
:)