Ruby 1.9's built in support of currying支持两种方法来处理一个带有任意数量参数的proc:
my_proc = proc {|*x| x.max }
1)curry
没有参数:my_proc.curry
。你将逗号分隔的参数传递给curried的proc,就像你对普通的proc一样。如果参数的数量是任意的(如果某些参数不是splat那么有用),这就无法实现正确的currying。
2)curry
带有参数:my_proc.curry(n)
这样,currying就会被应用,好像proc会接受n
个参数。例如:
my_proc.curry(3).call(2).call(5).call(1) #=> 5
那么,你将如何通过任意数量的参数实现讨论呢?这意味着,如果没有给出n
?
我想到的一种方法是通过代理call
收集参数,然后通过proc
解析method_missing
(如果使用call
以外的任何方法/ call
没有参数,使用收集的参数调用proc
,但我仍然在寻找其他方法来实现它。
更新
正如Andy H所说,问题是什么时候停止晃动。对于我的目的,如果调用停止/ proc评估何时调用除call
之外的任何方法或者在没有参数的情况下调用call
,那就没关系。
答案 0 :(得分:7)
内置curry
方法不适合您。原因是它产生一个proc,只要它有足够的参数就可以进行评估。引用您链接到的文档:
一个咖喱过程得到一些论点。如果提供了足够数量的参数,它会将提供的参数传递给原始proc并返回结果。
这里要实现的关键点是零是splat参数的“足够数量的参数”。
f = ->(x, y, z, *args){ [x, y, z, args] }
g = f.curry # => a "curryable" proc
h = g.call(1) # => another curryable proc
i = h.call(2) # => another curryable proc
j = i.call(3) # => [1, 2, 3, []]
如你所示,这些“可以理解”的过程可以一次一个地传递它们的参数,每次都返回一个新的curryable过程,直到有足够的参数传递,此时它会进行评估。这也是为什么他们不能支持任意长度的参数列表 - 它怎么知道何时停止currying并且只是评估?
如果你想要一种允许任意数量的参数的不同的currying方式,你可以定义自己的咖喱方法:
def my_curry(f, *curried_args)
->(*args) { f.call(*curried_args, *args) }
end
这是一个相当简单的实现,但可能符合您的目的。与内置方法的主要区别在于总是返回一个新的proc,即使已经传递了足够的参数,并且它不支持一次一个“咖喱链” ”
f = ->(x, y, z, *args) { [x, y, z, args] }
g = my_curry(f, 1) # => a proc
g.call(2, 3) # => [1, 2, 3, []]
g.call(2, 3, 4, 5) # => [1, 2, 3, [4, 5]]
g.call(2) # => ArgumentError: wrong number of arguments (2 for 3)
h = my_curry(g, 2, 3) # => a proc
h.call # => [1, 2, 3, []]
h.call(4, 5) # => [1, 2, 3, [4, 5]]
答案 1 :(得分:1)
我讨厌变得愚蠢,但忘了curry
并写下来
my_proc = proc { |*x| x.max }
l = -> *x { my_proc.( 4, 6, 12, 1, *x ) }
l.call + 1 #=> 13
你知道,Matz自己说curry
“只是一个功能性孩子的玩具”。它的实现并不特殊,它与您上面看到的并不快或不同。 Curry在Ruby中没那么有用......
对于那些不相信我说的人,这里是确切的引用和链接:
答案 2 :(得分:0)
我实现了一个使用Basic Object
的子类作为包装器来解决问题的解决方案:
class CurryWrap < BasicObject
def initialize proc
@proc = proc
@args = []
return self
end
def call *args
if args.size == 1
@args << args.first
return self
elsif args.empty?
return @proc.call(*@args)
else
@proc.call(*@args).call(*args)
end
end
def == other
@proc.call(*@args) == other
end
def method_missing m, *args, &block
@proc.call(*@args).send(m, *args, &block)
end
end
有了这个,基本上可以做到以下几点:
my_proc = proc { |*x| x.max }
CurryWrap.new(my_proc).call(4).call(6).call(12).call(1) + 1 #=> 13
除了带有参数的call
之外的任何内容都将解析proc。
我不知道这是否是最优雅的方式,所以我暂时不接受答案。