Ruby rcurry.我如何实现proc的“正确"操作弯腰? [英] Ruby rcurry. How I can implement proc "right" currying?

查看:61
本文介绍了Ruby rcurry.我如何实现proc的“正确"操作弯腰?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究一个新的宝石来扩展Ruby Core类,类似于主动支持核心扩展 Powerpack .如今,我正在研究 Proc 类,例如,我添加了封盖组成.

但是今天我喜欢谈论咖喱.这是 curry 的Ruby标准库功能:

 描述"#curry"它返回咖喱过程"模数=->(mod,num){num%mod}mod2 =模数.curry [2]期望(mod2.call(5)).到eq(1)结尾结尾 

我喜欢添加一个新的 rcurry 方法,以支持proc"right" currying通过以下规范.这是几个月前报告的同一问题,过程/方法#rcurry的工作方式类似于咖喱,但顺序相反.

 描述"#rcurry"它返回正确的咖喱过程"模数=->(mod,num){num%mod}mod2 =模数.rcurry [2]期望(mod2.call(5)).到eq(2)结尾结尾 

解决方案

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

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

这是您的规范的第一部分代码:

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

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

  class Procdef curry_bproc do | * passed |#???结尾结尾结尾 

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

这是代码的下一部分:

  mod2 =模数.curry.call(2)期望(mod2.call(5)).到eq(1) 

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

  def curry_bproc do | * passed |proc做| * args |#??结尾结尾结尾 

现在,当我们调用 mod2.call(5)时,最里面的块中的 args 的值将是 [5] ./p>

所以我们在 passed 中有 [2] ,在 args 中有 [5] .我们希望 mod2.call(5)的返回值与 modulus.call(2,5)的返回值相同,即> modulus.call(* passed,* args).在我们的方法中, modulus self ,所以我们只需要这样做:

  def curry_bproc做| * passed |proc做| * args |self.call(* passed,* args)结尾结尾结尾 

现在我们可以将其缩短一点并尝试一下:

  class Procdef curry_bproc do | * passed |proc {| * args |self [* passed,* args]}结尾结尾结尾mod2 =模数.curry_b [2]把mod2.call(5)#=>1个 

我们重新实现了 curry !那么,我们如何实现 rcurry ?好吧,唯一的区别是 rcurry 将咖喱参数放在末尾而不是开头,所以不管信不信由我们,我们只需要切换 passed args:

  class Proc递延proc do | * passed |proc {| * args |self [* args,* passed]}结尾结尾结尾模数=->(mod,num){num%mod}mod2 =模数.rcurry [2]p mod2.call(5)#=>2个 

当然,这并不是完美的:与 Proc#curry curry_b rcurry 不同, arity 参数.那应该不太难,所以我将它留给您练习.

I'm working in a new gem to extend the Ruby Core classes, something similar to Active Support Core Extensions or Powerpack. These days I'm working on the Proc class, for example I added closure composition.

But today I like to talk about currying. This is the Ruby standard library functionality for curry:

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

I like to add a new rcurry method to support proc "right" currying to pass the following spec. It's the same issue reported a few months ago, 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

解决方案

I think the way to solve this is to first solve an easier problem: How do we implement our owncurry?

One important thing you may or may not know is that Proc#[] is an alias for Proc#call, so in the above code modulus.curry[2] is the same as modulus.curry.call(2). Just for clarity I'm going to use .call in the code below.

Here's the first bit of code from your spec:

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

The second line tells us that modulus.curry returns a Proc (because we invoke call on it), so we know our method definition will look a bit like this:

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

Now when we call modulus.curry.call(2), the value of passed in the innermost block will be [ 2 ] (an array because we used the splat operator).

Here's the next part of your code:

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

Here we see that modulus.curry.call(2) itself returns a Proc, so now our method looks like this:

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

Now when we call mod2.call(5), the value of args in the innermost block will be [ 5 ].

So we have [ 2 ] in passed and [ 5 ] in args. We want the return value of mod2.call(5) to be the same as the return value of modulus.call(2, 5)—in other words, modulus.call(*passed, *args). In our method , modulus is self, so we just have to do this:

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

Now we can shorten it a bit and try it out:

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

We've reimplemented curry! How, then, do we implement rcurry? Well, the only difference is that rcurry puts the curried arguments at the end instead of the beginning, so believe it or not we just have to switch passed and args around:

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

Of course, this isn't perfect: Unlike Proc#curry curry_b and rcurry don't take an arity argument. That shouldn't be too tough, so I'll leave it as an exercise for you.

这篇关于Ruby rcurry.我如何实现proc的“正确"操作弯腰?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆