Friday, October 27, 2023

Clojure 取代 if 的技巧 (2) --- cond->

問題:

有時候,我們會想要表達一種語意 (semantic)

if (c) {
  hm[x] = y
}

這種語意在 Clojure 會略顯得冗長

(if c
    (assoc hm :x y)
    hm)

但是,其實可以這樣子表達

(cond-> hm
   c (assoc :x y))







Clojure 取代 if 的技巧 (1) --- fnil

問題:
我們需要對一個 Clojure HashMap hm 裡的某個 path 做操作,插入 val 值。需要滿足的條件為:

  • 如果該 path 為空,就插入空的 vector,並且在該 vector 裡插入 val 
  • 如果該 path 已經有一個 vector  ,就把 val 插入 vector 的尾端
直覺的寫法可能是:

(if (nil? (get-in hm path)
    (assoc-in hm path [val])
    (update-in hm path conj val))

但是,這又略顯得重複、冗長

解法:
  • (update hm path (fnil conj []) val)
  • (update-in hm path (fnil conj []) val)


Clojure 的 tree 表示法

我本來是比較喜歡 (2) ,多想了一陣子之後,又覺得 (1) 似乎更好一些些。

使用 (1) 的表示法還有一個理由: 假設,你現在是要從一堆已經 serialized data 裡,一個又一個的 item 去讀取,最後會建出這顆樹。很有可能,你會想寫 recursive function 。

而在 Clojure 裡,想到 recursive function ,要想到兩種改善效能的方式:
(1) recur
(2) lazy-seq

其中,lazy-seq 的話,需要跟 cons 一起搭配使用,所以 (1) 的表示法相對有彈性。