考虑一下,这很好用:
:>.to_proc.curry(2)[9][8] #=> true, because 9 > 8
但是,即使>
是一个二元运算符,如果没有指定arity,上面的代码也行不通:
:>.to_proc.curry[9][8] #=> ArgumentError: wrong number of arguments (0 for 1)
为什么这两个不等同?
注意:我特别想创建一个提供了一个arg的中间curried函数,然后调用然后用第二个arg调用它。
答案 0 :(得分:5)
:<.to_proc.arity # => -1
来自arity
的负值令人困惑,但基本上以某种方式表示“可变数量的参数”。
比较:
less_than = lambda {|a, b| a < b}
less_than.arity # => 2
当你创建一个说它需要两个参数的lambda时,它知道它需要两个参数,并且可以正常使用这种调用#curry的方式。
less_than.curry[9][8] # => false, no problem!
但是当你使用符号#to_proc技巧时,它只是有一个符号继续,它不知道它需要多少个参数。虽然我不认为<
实际上是ruby中的普通方法,但我认为你是对的,它必然需要两个args,Symbol#to_proc是一个适用于任何方法名称的通用方法,它有不知道该方法应该采用多少args,因此使用可变参数定义proc。
我没有很好地阅读C以遵循MRI实现,但我认为Symbol#to_proc定义了一个带有可变参数的proc。当然,Symbol#to_proc的更典型用法是无争论方法。例如,您可以使用它来执行此操作:
hello_proc = :hello.to_proc
class SomeClass
def hello(name = nil)
puts "Hello, #{name}!"
end
end
obj = SomeClass.new
obj.hello #=> "Hello, !"
obj.hello("jrochkind") #=> "Hello, jrochkind!"
obj.hello("jrochkind", "another")
# => ArgumentError: wrong number of arguments calling `hello` (2 for 1)
hello_proc.call(obj) # => "Hello, !"
hello_proc.call(obj, "jrochkind") # => "Hello, jrochkind!"
hello_proc.call(obj, "jrochkind", "another")
# => ArgumentError: wrong number of arguments calling `hello` (2 for 1)
hello_proc.call("Some string")
# => NoMethodError: undefined method `hello' for "Some string":String
注意我在定义hello_proc = :hello.to_proc
之前做了SomeClass
。 Symbol#to_proc机制创建一个变量arity proc,它不知道它将被调用的方式或位置或类,它创建一个可以在任何类上调用的proc,并且可以与任意数量的参数一起使用
如果 在ruby而不是C中定义,则它看起来像这样:
class Symbol
def to_proc
method_name = self
proc {|receiver, *other_args| receiver.send(method_name, *other_args) }
end
end
答案 1 :(得分:3)
我认为这是因为Symbol#to_proc
创建了一个带有一个参数的proc。变成proc时,:>
看起来不像:
->x, y{...}
但看起来像是:
->x{...}
要求原始单个参数>
以某种方式隐藏在proc体内(注意>
不是一个带有两个参数的方法,它是一个在一个接收器上调用的方法参数)。事实上,
:>.to_proc.arity # => -1
->x, y{}.arity # => 2
这意味着在没有参数的情况下将curry
应用于它只会产生微不足道的影响;它需要一个带有一个参数的proc,并返回自己。通过明确指定2
,它可以做一些非平凡的事情。为了进行比较,请考虑join
:
:join.to_proc.arity # => -1
:join.to_proc.call(["x", "y"]) # => "xy"
:join.to_proc.curry.call(["x", "y"]) # => "xy"
请注意,在Currying :join
之后提供单个参数已经评估了整个方法。
答案 2 :(得分:3)
@ jrochkind的回答很好地解释了为什么:>.to_proc.curry
没有你想要的行为。不过,我想提一下,这部分问题可以解决这个问题:
我特别想创建一个带有一个arg的中间curried函数,然后调用然后用第二个arg调用它。
解决方案是Object#method
。而不是:
nine_is_greater_than = :>.to_proc.curry[9]
nine_is_greater_than[8]
#=> ArgumentError: wrong number of arguments (0 for 1)
......这样做:
nine_is_greater_than = 9.method(:>)
nine_is_greater_than[8]
# => true
Object#method
返回一个Method对象,它就像一个Proc:它响应call
,[]
,甚至(从Ruby 2.2开始){{1 }}。但是,如果您需要真正的 proc(或者想要将curry
与Ruby&lt; 2.2一起使用),您也可以在其上调用curry
(或使用to_proc
,&
运算符):
to_proc