n-ary curry in CoffeeScript
n-ary curry in CoffeeScript
我在玩CoffeeScript时,发现自己写了下面几行,然后敬畏地看着它们:
compose = (f, g) -> (x) -> f g x
curry = (f) -> (x) -> (y) -> f(x, y)
uncurry = (f) -> (x, y) -> (f x) y
我觉得多好啊!现在,作为练习,我想将curry和uncurry函数概括为n个参数,以获得类似于下面的内容:
curry2 = (f) -> (x) -> (y) -> f(x, y)
curry3 = (f) -> (x) -> (y) -> (z) -> f(x, y, z)
curry4 = (f) -> (x) -> (y) -> (z) -> (t) -> f(x, y, z, t)
uncurry也是一样:
uncurry2 = (f) -> (x, y) -> (f x) y
uncurry3 = (f) -> (x, y, z) -> ((f x) y) z
uncurry4 = (f) -> (x, y, z, t) -> (((f x) y) z) t
写n元uncurry并不难:
uncurry = (n) -> (f) -> (args...) ->
if n == 1
f args[0]
else
((uncurry n - 1) f args.shift()) args...
另一方面,我不知道如何让n元咖喱工作。我想首先实现一个curry_list函数,它是这个套件的泛化:
curry_list2 = (f) -> (x) -> [x, y]
curry_list3 = (f) -> (x) -> (z) -> [x, y, z]
curry_list4 = (f) -> (x) -> (z) -> (t) -> [x, y, z, t]
实现如下:
curry_list = (n) ->
curry_list_accum = (n, accum) ->
if n
(x) ->
accum.push x
curry_list_accum n - 1, accum
else
accum
curry_list_accum n, []
然后我将curry_list与函数应用程序组合以获得柯里化。这就是我想做的:
curry = (n) ->
apply_helper = (f) -> (args) -> f args...
(f) -> compose (apply_helper f), (curry_list n)
但由于某种原因,它不起作用。例如,尝试对
求值curry(3)((a,b,c) -> a + b + c)(1)(2)(3)
产生以下错误:
Function.prototype。参数列表类型错误
现在,在写了更多的笔记之后,我明白了试图用curry_list组合f是不正确的。我确实有一种直觉,我要找的是看起来像这个组成的东西,但不完全是这样。我这样想对吗?
最后,什么是正确的实现?
您返回的是curry(3)((a,b,c) -> a + b + c)
之后的组合函数,而不是累加器。
这意味着((args) -> f args...)
正在接收一个函数作为参数,你的代码不会等到参数列表完成后再调用f
。
也许实现这个没有组合?
accumulator = (n, accum, f) ->
return f accum... if n is 0
(x) ->
accum.push x
accumulator n - 1, accum, f
curry = (n) ->
(f) -> accumulator n, [], f
curry(3)((a,b,c) -> a + b + c)(1)(2)(3) # 6
我认为没有比这更简单的了:
Function::curry = (n) ->
if n is 1 then @ else (x) => (=> @ x, arguments...).curry(n-1)
基本上,如果请求的实参数大于1,则它消耗一个实参并返回另一个函数,并在此过程中构造一个函数阶梯。它会一直这样做,直到函数的密度为1,在这种情况下,它会沿着它创建的函数的阶梯往上爬。梯子将消耗的参数添加到一个列表中,直到梯子顶部的函数(即用户提供的函数)被调用。
下面是我写的一些测试,如果你感兴趣的话:add3 = ((a,b,c) -> a+b+c).curry 3
add3_1 = add3 1
add3_1_2 = add3_1 2
console.log add3_1(4)(5) is 10
console.log add3_1(4)(6) is 11
console.log add3_1_2(4) is 7
console.log add3_1(5)(5) is 11
我看不像是构图。您拥有的最后一个curry
实现似乎没有区分要柯里化的函数和该函数的参数,然而,这样的区分似乎相当重要。像这样的怎么样?
curry = (n, f) ->
acc = []
helper = (x) ->
acc.push x
if acc.length is n then f acc... else helper
在偏自由变量中给出了一个相关的推广。
我对解决这个问题很感兴趣,所以我写了这个。它可能需要一些调整,但就我的测试而言,它工作得非常好。基本上只需调用f.c ry(),它会连续返回部分应用的函数。直到你用它接受的最后一个参数调用它,也就是当这个部分调用它之前的一个参数时,以此类推,一直到原来的f。
partial = (f, args1...) -> (args2...) ->
f.apply @, args1.concat args2
partial$ = (f, args) ->
partial.apply @, [f].concat args
Function::curry = (args...) ->
curry$ = (n, f, args...) ->
curry$$ = (n, f, args...) ->
if n > args.length
partial curry$$, (n - args.length), (partial$ f, args)
else f args
curry$$ (n - args.length), (partial$ f, args)
curry$.apply @, [@length, @].concat args
现在我们可以这样做:
twoToThe = Math.pow.curry 2
32 == twoToThe 5
32 == Math.pow.curry()(2)(5)
32 == Math.pow.curry()(2, 5)
32 == Math.pow.curry(2, 5)
32 == Math.pow.curry(2)(5)
# And just for laughs:
32 == Math.pow.curry()()()(2)()()()(5)
- Node.js's Buffer.writeFloatBE in Javascript
- Setting default onclick behavior for <img> tag in gene
- Lucene Search in Alfresco
- grep in JQuery to C#
- write HTML in JavaScript
- jQuery setTimeOut: in for-loop
- SetTimeout and clearTimeout in Javascript
- lightbox in html 5 and javascript
- HTMLInputElement in IE7
- Mongodb$in以与数组中相同的顺序获取结果
- element.dataset in Internet Explorer
- jQuery parent() in table
- Instagram oauth flow in angularjs
- In循环的In运算符前后
- CoffeeScript 在 'for v in values' 中创建一个全局变量 'v',导致事件中的错误引用
- CoffeeScript in server/server.coffee won't execute
- beginPath() in Coffeescript?
- n-ary curry in CoffeeScript
- Classes in CoffeeScript
- ?= vs ||= in CoffeeScript