Ruby rcurry。我怎样才能实现“正确”的流程?

时间:2015-12-04 01:44:10

标签: ruby lambda closures proc currying

我正在使用一个新的gem来扩展Ruby Core类,类似于Active Support Core ExtensionsPowerpack。这些天我正在研究Proc课程,例如我添加了closure composition

但今天我想谈谈讨论。这是curry

的Ruby标准库功能
describe "#curry" do
  it "returns a curried proc" do
    modulus = ->(mod, num) { num % mod }
    mod2 = modulus.curry[2]
    expect(mod2.call(5)).to eq(1)
  end
end

我想添加一个新的rcurry方法来支持proc“正确”currying以通过以下规范。这是几个月前报道的同一问题,Proc/Method#rcurry working like curry but in reverse order

describe "#rcurry" do
  it "returns a right curried proc" do
    modulus = ->(mod, num) { num % mod }
    mod2 = modulus.rcurry[2]
    expect(mod2.call(5)).to eq(2)
  end
end

1 个答案:

答案 0 :(得分:1)

我认为解决这个问题的方法是先解决一个更简单的问题:我们如何实现自己的curry

您可能知道或不知道的一件重要事情是Proc#[]Proc#call的别名,因此在上面的代码中modulus.curry[2]modulus.curry.call(2)相同。为了清楚起见,我将在下面的代码中使用.call

以下是您的规范中的第一部分代码:

modulus = ->(mod, num) { num % mod }
mod2 = modulus.curry.call(2)

第二行告诉我们modulus.curry返回一个Proc(因为我们在其上调用call),所以我们知道我们的方法定义看起来有点像这样:

class Proc
  def curry_b
    proc do |*passed|
      # ???
    end
  end
end

现在,当我们调用modulus.curry.call(2)时,最里面的块中passed的值将为[ 2 ](数组,因为我们使用了splat运算符)。

这是代码的下一部分:

mod2 = modulus.curry.call(2)
expect(mod2.call(5)).to eq(1)

在这里,我们看到modulus.curry.call(2)本身返回一个Proc,所以现在我们的方法如下所示:

def curry_b
  proc do |*passed|
    proc do |*args|
      # ??
    end
  end
end

现在,当我们致电mod2.call(5)时,最里面的区块中args的值将为[ 5 ]

我们[ 2 ]中的passed[ 5 ]中的args。我们希望mod2.call(5)的返回值与modulus.call(2, 5)的返回值相同 - 换句话说,modulus.call(*passed, *args)。在我们的方法中,modulusself,因此我们必须这样做:

def curry_b
  proc do |*passed|
    proc do |*args|
      self.call(*passed, *args)
    end
  end
end

现在我们可以缩短它并尝试一下:

class Proc
  def curry_b
    proc do |*passed|
      proc {|*args| self[*passed, *args] }
    end
  end
end

mod2 = modulus.curry_b[2]
puts mod2.call(5)
# => 1

我们重新实现了curry!那么,我们如何实施rcurry?好吧,唯一的区别是rcurry将讨论的论点放在最后而不是开头,所以不管你信不信,我们只需要转换passedargs

class Proc
  def rcurry
    proc do |*passed|
      proc {|*args| self[*args, *passed] }
    end
  end
end

modulus = ->(mod, num) { num % mod }
mod2 = modulus.rcurry[2]
p mod2.call(5)
# => 2

当然,这并不完美:与Proc#curry curry_brcurry不同,不要采用 arity 参数。这不应该太难,所以我会把它作为锻炼给你。