文章出處

前言

 習慣了Ramda.js就會潛意識地認為函數均已柯里化,然后就可以隨心所欲的用函數生成函數,或者使用compose組合多個函數來生成一個新函數。如下

const f = a => b => a + b
const g = c => d => c - d
const compose = f => g => x => f(g(x))

const add1 = f(1)
add1(2) // 返回3

const addThenMinus = compose(g(2), f(1))
addThenMinus(3) // 返回-2

 ES6的arrow function讓我們輕易寫出柯里化的函數(當然使用Ramda.js會更輕松),若換成ES5就蛋痛很多了。而不幸的是cljs采納和js一樣能夠接受可變參數的函數特性,這使得其必須拋棄如haskell函數自動柯里化的特性。若用cljs實現上述代碼將會如此地丑陋

(defn f [a]
    (fn [b] (+ a b)))
(defn g [c]
    (fn [d] (- c d)))

(def add1 (f 1))

 那么要如何才能在cljs中優美地實現柯里化呢?答案是兩步走:

  1. 實現Ramda.js中R.curry函數的cljs版
  2. 借助curry函數實現macro

實現curry函數

;; 定義
(defn curry
  [f n & args]
  (fn [& more]
    (let [a (vec (concat args more))]
      (if (> n (count a))
        (apply curry (reduce conj [f n] a))
        (apply f (take n a))))))

;; 使用
(defn f [a b]
    (+ a b))
(def fc (curry f 2))
(def add1 (fc 1))

實現defnc宏

;; 定義
(defmacro defnc [name args & body]
  {:pre [(not-any? #{\&} args)]}
  (let [n (count args)]
  `(def ~name
     (curry
       (fn ~args ~@body)
       ~n))))

;; 使用
(defnc f [a b]
    (+ a b))
(def add1 (f 1))

總結

 cljs中的macro讓我們可以靈活擴展語言特性,真是越用越酸爽啊!
尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/7103601.html ^_^肥仔John


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()