(cljs/run-at (JSVM. :all) "一起实现柯里化")
前言
习惯了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中优美地实现柯里化呢?答案是两步走:
- 实现Ramda.js中
R.curry
函数的cljs版 - 借助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
欢迎添加我的公众号一起深入探讨技术手艺人的那些事!
如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!