iT邦幫忙

2022 iThome 鐵人賽

DAY 10
0

[Day10] Clojure Functional Programming與資料操作(3) - filter

在前天和昨天文章介紹了mapreduce之後,
相信大家都對於資料操作上有一點手感,
以及對於怎麼把函式回傳的東東往下一個步驟傳的寫法更熟悉了吧?

(reduce + (vec (range 1 101)))
=> 5050

(reduce + (range 1 101))
=> 5050

(reductions + (range 1 101))

=> (1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 210 231 253 276 300 325 351 378 406 435 465 496 528 561 595 630 666 703 741 780 820 861 903 946 990 1035 1081 1128 1176 1225 1275 1326 1378 1431 1485 1540 1596 1653 1711 1770 1830 1891 1953 2016 2080 2145 2211 2278 2346 2415 2485 2556 2628 2701 2775 2850 2926 3003 3081 3160 3240 3321 3403 3486 3570 3655 3741 3828 3916 4005 4095 4186 4278 4371 4465 4560 4656 4753 4851 4950 5050)

昨天的最後我們甚至寫了自己的sum

(def sum #(reduce + %))

(sum [1 3 5 7 9])
=> 25

(sum(vec(range 1 101)))
=> 5050

(sum(range 1 101))
=> 5050

看來目前最高的紀錄是至少已經駕馭了同時有三對括弧的存在~ :P
再也不害怕一堆括弧了~YA!

有了這個層面的自信(看懂括弧pair代表的data structure意涵)
來開始學第三個操作資料好用的function filter

filter 用法

當符合前面filter (predicate item,描述的item)條件邏輯為true時,
回傳collection裡面的item變成 lazy sequence

filter: 作用於不同的data structure

filter & Sequences

以上篇文章reduce的例子來比對寫法,
我們可以很輕易地依樣畫葫蘆用filter來舉例

(reduce    +  (range 1 101))
(filter even? (range 1 50))

例如,

  • 先用range產生一組Numbers Sequence

  • 然後參考我最常用的clojure小抄,找一個常使用的條件function,

(filter even? (range 50)) range第一個參數省略代表從0開始:

(filter even? (range 50))
 
=> (0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98)

range第一個參數也可以不省略:

(filter even? (range -50 50))
 
=> (-50 -48 -46 -44 -42 -40 -38 -36 -34 -32 -30 -28 -26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48)

本系列文的看倌程度都很優秀,用Number sequence當filter實在是太小看大家啦!
來舉一些複雜一點的例子:

filter & Maps

小妹我多年前曾經在澳洲四處遊蕩一陣子,有一年的工作是在農場,偶爾的任務是需要餵牛趕羊

所以我打算用Map data structure來裝起農場裡動物們的數量

{:cat 5 :dog 3 :chicken 15 :cow 22 :sheep 46 }

那我想找出偶數的動物種類,該怎麼寫這樣的clojure呢?

首先從Day6的文章的code了解到,
Map collection有key和value,

(defn creating-map
  [key value]
  {(keyword key) value})

參考匿名函式的做法:

(def a-func (fn [x y] (+ x y)))

先把key value從map裡destruct出來,
讓我們簡潔地去獲取一些collection內的element

(這裡先稍安勿躁~之後預計也會再花一天的版幅去說明destructure)

;; 寫法
(filter pre collection)

把map傳給predicate item(在此例的pred是匿名函式)做判斷,
filter出value是偶數的item
符合條件的裝回vector

(filter (fn [[key value]]
(even? value))
{:cat 5 :dog 3 :chicken 15 :cow 22 :sheep 46 })

=> ([:cow 22] [:sheep 46])

如果想要再跟原本一樣回傳排完序的Map collection,可以使用into

(into (sorted-map)(filter (fn [[key value]]
(even? value))
{:cat 5 :dog 3 :chicken 15 :cow 22 :sheep 46 }))

=> {:cow 22, :sheep 46}

農場裡偶數的動物有牛22隻、羊46隻~

跟filter類似的其他寫法

上篇文章的後續延伸思考,前輩有給一些跟reduce類似的語法,

例如

(reduce + (range 1 101))
;;
(apply + (range 1 101)) 

兩個結果一樣

因此我在這篇文章也來試著舉一反三,多開開眼界 :P

舉一反二: filter 搭配 partial / comp

((comp (partial into {})
(partial filter (comp even? val)))
{:cat 5 :dog 3 :chicken 15 :cow 22 :sheep 46 })

=> {:cow 22, :sheep 46}

舉一反三: select-keys

(let [map {:cat 5 :dog 3 :chicken 15 :cow 22 :sheep 46 }]
  (select-keys map (for [[key value] map :when (even? value)] key)))
  
=>{:cow 22, :sheep 46}

雖然clojure超新星目前還很難比較各種寫法的優劣和取捨,
不過隨著每次多看、多查一些用法,相信眼界會越來越開闊,有一天也能融會貫通的(握拳)!

最後送上農場裡剛生出來的puppies可愛圖做收尾~

大家週末愉快啦!(揮手)


上一篇
[Day09] Clojure Functional Programming與資料操作(2) - reduce
下一篇
[Day11] Clojure Flow Control (1) 基礎篇 if / if-not / for
系列文
後端Developer實戰ClojureScript: Reagent與前端框架 Reframe30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言