Proc.arity vs Lambda.arity

后端 未结 3 997
小蘑菇
小蘑菇 2021-02-19 08:22

Why do proc and lambda return different values for arity?

e.g.

proc   { |x = 0| }.arity       #=> 0
lambda { |a = 0| }.arity       #=> -1
proc   {         


        
相关标签:
3条回答
  • 2021-02-19 09:09

    As mentioned here:(Differences between Proc and Lambda), one of the primary differences between procs and lambda are that "Just like methods, lambdas have strict argument checking, whereas non-lambda Procs have loose argument checking, just like blocks."

    Thus, since the arity is based on the number of required arguments, this will change between procs and lambdas.

    0 讨论(0)
  • 2021-02-19 09:19

    After reading the other 2 answers, my guess is that in #arity method is treading thin ice. For fixed number of ordered arguments, #arity used to be perfectly OK method. Then, when optional arguments were added, to stick with arity representation by a single integer, minus sign was exploited as a flag. But already, argument field information is being discarded, as eg. 1ary or 2ary -> a, b=1 { a + b } indicates same arity (-2) as -> a, *b { a + b.sum }, taking 1 to arbitrary number of arguments. After the change of #arity behavior in 1.9, another blow comes in 2.0, where named arguments are introduced, which go completely unnoticed by #arity. Again, there will be compulsory and optional named arguments, plus the possibility of collecting arbitrary number of them with hash splash **. I would expect #arity method to change its behavior again in the future...

    0 讨论(0)
  • 2021-02-19 09:27

    Per the docs you linked to:

    Returns the number of arguments that would not be ignored. If the block is declared to take no arguments, returns 0. If the block is known to take exactly n arguments, returns n. If the block has optional arguments, return -n-1, where n is the number of mandatory arguments. A proc with no argument declarations is the same a block declaring || as its arguments.

    What the doc forgets to mention is that procs and lambda don't treat arguments precisely the same way e.g.:

    >> p = proc { |a = 1, b| b }
    => #<Proc:0x007ff0091ef810@(irb):1>
    >> l = lambda { |a = 1, b| b }
    => #<Proc:0x007ff0098099f8@(irb):2 (lambda)>
    >> p.call
    => nil
    >> l.call
    ArgumentError: wrong number of arguments (0 for 1..2)
        from (irb):2:in `block in irb_binding'
        from (irb):4:in `call'
        from (irb):4
        from /usr/local/bin/irb:12:in `<main>'
    

    Edit: The Ruby Programming Language, from O'Reilly, is the one with a tiny bit more details:

    6.5.3 The Arity of a Proc

    The arity of a proc or lambda is the number of arguments it expects. (The word is derived from the “ary” suffix of unary, binary, ternary, etc.) Proc objects have an arity method that returns the number of arguments they expect. For example:

    lambda{||}.arity # => 0. No arguments expected
    lambda{|x| x}.arity # => 1. One argument expected
    lambda{|x,y| x+y}.arity # => 2. Two arguments expected
    

    The notion of arity gets confusing when a Proc accepts an arbitrary number of argu- ments in an *-prefixed final argument. When a Proc allows optional arguments, the arity method returns a negative number of the form -n-1. A return value of this form indicates that the Proc requires n arguments, but it may optionally take additional arguments as well. -n-1 is known as the one’s-complement of n, and you can invert it with the ~ operator. So if arity returns a negative number m, then ~m (or -m-1) gives you the number of required arguments:

    lambda {|*args|}.arity # => -1. ~-1 = -(-1)-1 = 0 arguments required
    lambda {|first, *rest|}.arity # => -2. ~-2 = -(-2)-1 = 1 argument required
    

    There is one final wrinkle to the arity method. In Ruby 1.8, a Proc declared without any argument clause at all (that is, without any || characters) may be invoked with any number of arguments (and these arguments are ignored). The arity method returns –1 to indicate that there are no required arguments. This has changed in Ruby 1.9: a Proc declared like this has an arity of 0. If it is a lambda, then it is an error to invoke it with any arguments:

    puts lambda {}.arity # –1 in Ruby 1.8; 0 in Ruby 1.9
    

    Edit 2: Stefan adds the precise reason they differ in a comment:

    http://www.ruby-doc.org/core-2.0/Proc.html#method-i-call

    For procs created using lambda or ->() an error is generated if the wrong number of parameters are passed to a Proc with multiple parameters. For procs created using Proc.new or Kernel.proc, extra parameters are silently discarded.

    0 讨论(0)
提交回复
热议问题