When to use lambda, when to use Proc.new?

2019-01-02 19:06发布

In Ruby 1.8, there are subtle differences between proc/lambda on the one hand, and Proc.new on the other.

  • What are those differences?
  • Can you give guidelines on how to decide which one to choose?
  • In Ruby 1.9, proc and lambda are different. What's the deal?

标签: ruby lambda proc
15条回答
看风景的人
2楼-- · 2019-01-02 19:35

I can't say much about the subtle differences. However, I can point out that Ruby 1.9 now allows optional parameters for lambdas and blocks.

Here's the new syntax for the stabby lambdas under 1.9:

stabby = ->(msg='inside the stabby lambda') { puts msg }

Ruby 1.8 didn't have that syntax. Neither did the conventional way of declaring blocks/lambdas support optional args:

# under 1.8
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }

Ruby 1.9, however, supports optional arguments even with the old syntax:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

If you wanna build Ruby1.9 for Leopard or Linux, check out this article (shameless self promotion).

查看更多
刘海飞了
3楼-- · 2019-01-02 19:37

I am a bit late on this, but there is one great but little known thing about Proc.new not mentioned in comments at all. As by documentation:

Proc::new may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object.

That said, Proc.new lets to chain yielding methods:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
查看更多
笑指拈花
4楼-- · 2019-01-02 19:38

I found this page which shows what the difference between Proc.new and lambda are. According to the page, the only difference is that a lambda is strict about the number of arguments it accepts, whereas Proc.new converts missing arguments to nil. Here is an example IRB session illustrating the difference:

irb(main):001:0> l = lambda { |x, y| x + y }
=> #<Proc:0x00007fc605ec0748@(irb):1>
irb(main):002:0> p = Proc.new { |x, y| x + y }
=> #<Proc:0x00007fc605ea8698@(irb):2>
irb(main):003:0> l.call "hello", "world"
=> "helloworld"
irb(main):004:0> p.call "hello", "world"
=> "helloworld"
irb(main):005:0> l.call "hello"
ArgumentError: wrong number of arguments (1 for 2)
    from (irb):1
    from (irb):5:in `call'
    from (irb):5
    from :0
irb(main):006:0> p.call "hello"
TypeError: can't convert nil into String
    from (irb):2:in `+'
    from (irb):2
    from (irb):6:in `call'
    from (irb):6
    from :0

The page also recommends using lambda unless you specifically want the error tolerant behavior. I agree with this sentiment. Using a lambda seems a tad more concise, and with such an insignificant difference, it seems the better choice in the average situation.

As for Ruby 1.9, sorry, I haven't looked into 1.9 yet, but I don't imagine they would change it all that much (don't take my word for it though, it seems you have heard of some changes, so I am probably wrong there).

查看更多
若你有天会懂
5楼-- · 2019-01-02 19:39

Short answer: What matters is what return does: lambda returns out of itself, and proc returns out of itself AND the function that called it.

What is less clear is why you want to use each. lambda is what we expect things should do in a functional programming sense. It is basically an anonymous method with the current scope automatically bound. Of the two, lambda is the one you should probably be using.

Proc, on the other hand, is really useful for implementing the language itself. For example you can implement "if" statements or "for" loops with them. Any return found in the proc will return out of the method that called it, not the just the "if" statement. This is how languages work, how "if" statements work, so my guess is Ruby uses this under the covers and they just exposed it because it seemed powerful.

You would only really need this if you are creating new language constructs like loops, if-else constructs, etc.

查看更多
心情的温度
6楼-- · 2019-01-02 19:41

A good way to see it is that lambdas are executed in their own scope (as if it was a method call), while Procs may be viewed as executed inline with the calling method, at least that's a good way of deciding wich one to use in each case.

查看更多
ら面具成の殇う
7楼-- · 2019-01-02 19:44

Another important but subtle difference between procs created with lambda and procs created with Proc.new is how they handle the return statement:

  • In a lambda-created proc, the return statement returns only from the proc itself
  • In a Proc.new-created proc, the return statement is a little more surprising: it returns control not just from the proc, but also from the method enclosing the proc!

Here's lambda-created proc's return in action. It behaves in a way that you probably expect:

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"

Now here's a Proc.new-created proc's return doing the same thing. You're about to see one of those cases where Ruby breaks the much-vaunted Principle of Least Surprise:

def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

Thanks to this surprising behavior (as well as less typing), I tend to favor using lambda over Proc.new when making procs.

查看更多
登录 后发表回答