(cljs/run-at (JSVM. :all) "一起實現柯里化")

^_^肥仔John發表於2017-07-02

前言

 習慣了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

相關文章