Ruby rcurry.我如何实现proc的“正确"操作弯腰? [英] Ruby rcurry. How I can implement proc "right" currying?
问题描述
我正在研究一个新的宝石来扩展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屋!