【问题标题】:n-ary curry in CoffeeScriptCoffeeScript 中的 n 元咖喱
【发布时间】:2012-08-17 14:10:18
【问题描述】:

当我发现自己在写以下几行然后敬畏地看着它们时,我正在玩 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 args,以获得类似的东西:

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.apply: 参数列表类型错误

现在在记了一些笔记之后,我明白尝试用 curry_list 组合 f 是不正确的。我确实有直觉,我正在寻找的是看起来像这个组合的东西,但不完全是那样。我这样想对吗?

最后,正确的实现是什么?

【问题讨论】:

  • 我很敬畏你的问题。 +1,好先生。
  • 我不久前写的这个 JS 实现可能会为你指明正确的方向:codingforums.com/showthread.php?t=138769
  • +1 严重混淆了我。有那么一刻我以为我在看haskell。

标签: javascript functional-programming coffeescript


【解决方案1】:

您在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

【讨论】:

  • 确实!谢谢你的解释。
【解决方案2】:

我认为没有比这更简单的了:

Function::curry = (n) ->
  if n is 1 then @ else (x) => (=> @ x, arguments...).curry(n-1)

基本上,如果请求的 arity 大于 1,那么它会消耗一个参数并返回另一个函数,并在此过程中构造一个函数阶梯。它会一直这样做,直到 arity 为 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

【讨论】:

    【解决方案3】:

    在我看来它不像作曲。您拥有的最后一个curry 实现似乎没有区分要柯里化的函数和该函数的参数,而这种区别似乎相当重要。这样的事情怎么样?

    curry = (n, f) ->
        acc = []
        helper = (x) ->
            acc.push x
            if acc.length is n then f acc... else helper
    

    【讨论】:

    • 优雅的答案!在问题中,我以 curried 样式编写了函数,所以 curry = (n) -> (f) -> 做的完全一样。
    【解决方案4】:

    Partial with Free Variables 中提供了相关的概括。

    【讨论】:

    • 对了,在partialFree实现中,为什么b反了? func (for arg in a then arg ?= b.shift())... 不会吗?
    【解决方案5】:

    我真的很想解决这个问题,所以我写了这个。它可能需要一些调整,但就我测试过的而言,它的效果非常好。基本上只需调用 f.curry() ,它会连续返回部分应用的函数.. 直到你用它所接受的最后一个参数调用它,也就是当那个部分调用它之前的那个时,依此类推,一直回到原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)
    

    【讨论】:

      猜你喜欢
      • 2018-08-13
      • 1970-01-01
      • 2018-02-02
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 1970-01-01
      • 2018-11-09
      相关资源
      最近更新 更多