为什么必须明确指定2个参数来进行咖喱:>

时间:2016-01-29 03:41:28

标签: ruby

考虑一下,这很好用:

:>.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调用它。

3 个答案:

答案 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