今天來點小心得。
在學習 clojure 時,其作為一門 function 的語言,clojure 擁有 funcctional programing language 的幾個特性:
一旦建立,就不會隨意被改動,這讓程式的狀態更容易推導。
相較於 OOP 世界裡物件的屬性常常會被更新、覆蓋,Clojure 強調「用新的值產生新的資料結構」。
函數可以被當成參數傳遞、作為回傳值,甚至存在集合裡。
在 Ruby 或 JavaScript 中雖然也能寫匿名函數、Lambda,但 Clojure 把這種設計推到極致,幾乎所有東西都圍繞在「函數」本身。(也就是 clojure 中的一堆括號 ()
)
透過 map、reduce、filter、threading macros 等方式,描述「要做什麼」,而不是「怎麼做」。
OOP 世界的習慣則是「物件 + 方法」一步步改變狀態,偏向命令式(imperative)。
對於初學的我來說,第 1 & 3 點是需要經驗累積去感受的,所以還不太能體會。第一個有感受的反而是「函數能當回傳值」這件事。
因為我大多數經驗來自 Ruby(OOP),以及一些簡單的 JavaScript。在 Ruby 世界,函數(ruby 的世界是叫 method)是一段被呼叫的程式,回傳值即是 method 最後一行的 object,並沒有辦法將方法當作參數傳遞(當然也無法呼叫自己)。
但在 Clojure,函數本身可以呼叫自己(recursion),一開始覺得很新鮮,也忍不住常常想用遞迴解題。
後來才發現:
其實 Clojure 提供了很多更直觀的方法,不一定要靠 recursion。像 reduce、filter 搭配 threading macro,就能寫得簡單又清楚,效能也更好。
以 Day 2 題目(文章連結)為例,我也有嘗試用 recursion 來解。
遞迴雖然能跑出正確答案,但程式顯得冗長,也比較不好閱讀。
後來觀察其他人的寫法,發現只要透過:
threading last (->>):讓資料一步步流經不同的函數
filter:篩選需要的資料
reduce:累加或聚合結果
就能把原本的遞迴邏輯,濃縮成簡短、清晰的一段程式碼。
不僅意圖清楚,也完全不需要自己去處理「終止條件」或「遞迴深度」(影響吃掉的資源)這些額外負擔。
一點小小心得,其實應該是 1 & 2 & 3 2 都要有體會的,但我道行還不夠啦,先這麼一點點紀錄了。