Ruby 咖喱。我如何实现 proc "right" 柯里化?
Ruby rcurry. How I can implement proc "right" currying?
我正在新的 gem 中工作以扩展 Ruby 核心 类,类似于 Active Support Core Extensions or Powerpack. These days I'm working on the Proc
class, for example I added closure composition。
但是今天我想谈谈柯里化。这是 Ruby 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
我想添加一个新的 rcurry
方法来支持 proc "right" 柯里化以通过以下规范。这与几个月前报告的问题相同,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
我认为解决这个问题的方法是首先解决一个更简单的问题:我们如何实现我们自己的curry
?
一件您可能知道也可能不知道的重要事情是 Proc#[]
是 Proc#call
的别名,因此在上面的代码中 modulus.curry[2]
与 modulus.curry.call(2)
相同.为清楚起见,我将在下面的代码中使用 .call
。
这是您规范中的第一段代码:
modulus = ->(mod, num) { num % mod }
mod2 = modulus.curry.call(2)
第二行告诉我们 modulus.curry
return 是一个 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)
本身 return 是一个 Proc,所以现在我们的方法看起来像这样:
def curry_b
proc do |*passed|
proc do |*args|
# ??
end
end
end
现在当我们调用mod2.call(5)
时,最里面的块args
的值将是[ 5 ]
。
所以我们在 passed
中有 [ 2 ]
,在 args
中有 [ 5 ]
。我们希望 mod2.call(5)
的 return 值与 modulus.call(2, 5)
的 return 值相同——换句话说,modulus.call(*passed, *args)
。在我们的方法中,modulus
是 self
,所以我们只需要这样做:
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
将 curried 参数放在末尾而不是开头,所以不管你信不信我们只需要切换 passed
和 args
左右:
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_b
和 rcurry
不同,不要使用 arity 参数。这应该不会太难,所以我会把它作为练习留给你。
我正在新的 gem 中工作以扩展 Ruby 核心 类,类似于 Active Support Core Extensions or Powerpack. These days I'm working on the Proc
class, for example I added closure composition。
但是今天我想谈谈柯里化。这是 Ruby 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
我想添加一个新的 rcurry
方法来支持 proc "right" 柯里化以通过以下规范。这与几个月前报告的问题相同,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
我认为解决这个问题的方法是首先解决一个更简单的问题:我们如何实现我们自己的curry
?
一件您可能知道也可能不知道的重要事情是 Proc#[]
是 Proc#call
的别名,因此在上面的代码中 modulus.curry[2]
与 modulus.curry.call(2)
相同.为清楚起见,我将在下面的代码中使用 .call
。
这是您规范中的第一段代码:
modulus = ->(mod, num) { num % mod }
mod2 = modulus.curry.call(2)
第二行告诉我们 modulus.curry
return 是一个 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)
本身 return 是一个 Proc,所以现在我们的方法看起来像这样:
def curry_b
proc do |*passed|
proc do |*args|
# ??
end
end
end
现在当我们调用mod2.call(5)
时,最里面的块args
的值将是[ 5 ]
。
所以我们在 passed
中有 [ 2 ]
,在 args
中有 [ 5 ]
。我们希望 mod2.call(5)
的 return 值与 modulus.call(2, 5)
的 return 值相同——换句话说,modulus.call(*passed, *args)
。在我们的方法中,modulus
是 self
,所以我们只需要这样做:
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
将 curried 参数放在末尾而不是开头,所以不管你信不信我们只需要切换 passed
和 args
左右:
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_b
和 rcurry
不同,不要使用 arity 参数。这应该不会太难,所以我会把它作为练习留给你。