何时使用lambda,何时使用Proc.new?

在Ruby 1.8中,proc / lambda和Proc.new之间有细微差别。

  • 这些差异是什么?
  • 你可以给如何决定哪一个select的指导方针?
  • 在Ruby 1.9中,proc和lambda是不同的。 这是怎么回事?

使用Proc.new创build的使用lambdaProc.new创build的Proc.new之间的另一个重要但微妙的区别是它们如何处理return语句:

  • 在一个lambda创build的proc中, return语句只能从proc本身返回
  • 在一个Proc.new创build的过程中, return语句有点令人惊讶:它不仅从proc返回控制权, 而且还从包含proc的方法返回控制权

这是lambda创build的proc return的行动。 它的行为方式,你可能期望:

 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" 

现在,这是一个Proc.new创build的proc return做同样的事情。 你将会看到Ruby打破了最令人惊讶的原则之一:

 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" 

由于这种令人惊讶的行为(以及较less的打字),我倾向于在制作特效时倾向于使用比Proc.new lambda

为了进一步澄清:

Joey说, Proc.new的回报行为令人惊讶。 然而,当你认为Proc.new的行为像一个块,这并不奇怪,因为这正是块的行为。 另一方面,羔羊performance得更像方法。

这实际上解释了为什么Procs在灵活性(参数数量)方面很灵活,而lambdas却不是。 块不要求提供所有的参数,但方法(除非提供了默认值)。 虽然提供lambda参数的默认值在Ruby 1.8中不是一个选项,但现在在Ruby 1.9中使用替代的lambda语法(如webmat所示)支持:

 concat = ->(a, b=2){ "#{a}#{b}" } concat.call(4,5) # => "45" concat.call(1) # => "12" 

而Michiel de Mare(OP)对Ruby和Ruby 1.9中的Procs和lambdaperformance也是一样的错误。 我已经证实,他们仍然保持从上面指定的1.8的行为。

break语句在Procs或者lambdaexpression式中并没有多less意义。 在Procs中,rest会从你已经完成的Proc.new中返回。 从lambda打破,因为它本质上是一个方法,没有任何意义,你永远不会从一个方法的顶层破坏。

nextredoraise在Procs和lambdas中performance相同。 而任何一方都不允许retry ,并会引发例外。

最后, proc方法不应该被使用,因为它是不一致的,并且具有意想不到的行为。 在Ruby 1.8中它实际上返回一个lambda! 在Ruby 1.9中,这个问题已经修复,并且返回一个Proc。 如果你想创build一个Proc,坚持Proc.new

欲了解更多信息,我强烈build议O'Reilly的Ruby编程语言 ,这是我的大部分信息的来源。

我发现这个页面显示了Proc.newlambda之间的区别。 根据页面,唯一的区别是一个lambda是严格的参数的数量接受,而Proc.new转换缺less的参数nil 。 以下是IRB会议的一个例子,说明不同之处:

  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:错误的参数个数(1表示2)
    来自(irb):1
    来自(irb):5:在“通话”
    来自(irb):5
    从:0
 irb(main):006:0> p.call“hello”
 TypeError:不能将nil转换成String
    来自(irb):2:在“+”
    来自(irb):2
    来自(irb):6:在`call'
    来自(irb):6
    从:0 

该页面还build议使用lambda,除非您特别需要容错行为。 我同意这个观点。 使用lambda似乎更简洁一点,并且有如此微不足道的差异,在平均情况下似乎是更好的select。

对于Ruby 1.9来说,对不起,我还没有考虑到1.9,但我不认为他们会改变这一切(尽pipe我不听我的话,似乎你已经听到了一些变化,所以我可能在那里错了)。

Proc年龄较大,但回归的语义对我来说是非常违反直觉的(至less在我学习这门语言的时候)是因为:

  1. 如果你使用proc,你很可能使用某种function范例。
  2. Proc可以从封闭的范围返回(见前面的回答),这基本上是一个goto,而且是非function性的。

Lambda在function上更安全,更容易推理 – 我总是使用它来代替proc。

我不能说很多细微的差异。 不过,我可以指出,Ruby 1.9现在允许lambdas和块的可选参数。

下面是1.9下的stabby lambda的新语法:

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

Ruby 1.8没有这个语法。 传统的声明块/ lambdas的方式也不支持可选的参数:

 # 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支持可选参数,即使使用旧的语法:

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

如果你想为Leopard或者Linux构buildRuby1.9,看看这篇文章 (无耻的自我推销)。

简单的回答:重要的是return是什么:lambda返回自身,并且proc返回自身和调用它的函数。

不太清楚的是为什么你要使用每个。 lambda是我们期望事物在function编程意义上应该做的。 它基本上是一个匿名方法,当前范围自动绑定。 在这两个中,lambda是你应该使用的那个。

另一方面,Proc对于实现语言本身非常有用。 例如,你可以实现“if”语句或“for”循环。 在proc中find的任何返回将返callback用它的方法,而不是只是“if”语句。 这就是语言是如何工作的,“if”语句是如何工作的,所以我的猜测是Ruby在这个封面下使用了这个语言,他们只是暴露了它,因为它看起来很强大。

如果你正在创build像循环,if-else结构等新的语言结构,你只会真的需要这个。

一个好的方法是,lambdaexpression式是在他们自己的范围内执行的(就好像是一个方法调用一样),而Procs可以被视为与调用方法内联执行,至less这是决定使用哪一个的好方法在每种情况下。

我没有注意到在第三种方法中的评论,“proc”已被弃用,但在1.8和1.9中处理方式不同。

这里有一个相当详细的例子,可以很容易地看到三个类似的调用之间的差异:

 def meth1 puts "method start" pr = lambda { return } pr.call puts "method end" end def meth2 puts "method start" pr = Proc.new { return } pr.call puts "method end" end def meth3 puts "method start" pr = proc { return } pr.call puts "method end" end puts "Using lambda" meth1 puts "--------" puts "using Proc.new" meth2 puts "--------" puts "using proc" meth3 

Ruby中的闭包很好地概述了Ruby,block,lambda和proc如何在Ruby中工作。

Robert Sosinski对Ruby Blocks, Procs 和 Lambdas的理解清楚地解释了这些编程概念,并用示例代码加强了解释。 方法对象也是相关和覆盖的。

lambda按预期工作,就像其他语言一样。

有线Proc.new是令人惊讶和混乱。

Proc.new创build的proc中的return语句不仅仅是从它本身返回控制权, 也是从包含它的方法返回控制权。

 def some_method myproc = Proc.new {return "End."} myproc.call # Any code below will not get executed! # ... end 

您可以争辩说, Proc.new将代码插入封闭的方法,就像块一样。 但Proc.new创build一个对象,而块是对象的一部分

lambda和Proc.new之间还有另外一个区别,那就是它们处理(错误的)参数。 lambda抱怨它,而Proc.new忽略额外的论据或认为没有论据作为零。

 irb(main):021:0> l = -> (x) { x.to_s } => #<Proc:0x8b63750@(irb):21 (lambda)> irb(main):022:0> p = Proc.new { |x| x.to_s} => #<Proc:0x8b59494@(irb):22> irb(main):025:0> l.call ArgumentError: wrong number of arguments (0 for 1) from (irb):21:in `block in irb_binding' from (irb):25:in `call' from (irb):25 from /usr/bin/irb:11:in `<main>' irb(main):026:0> p.call => "" irb(main):049:0> l.call 1, 2 ArgumentError: wrong number of arguments (2 for 1) from (irb):47:in `block in irb_binding' from (irb):49:in `call' from (irb):49 from /usr/bin/irb:11:in `<main>' irb(main):050:0> p.call 1, 2 => "1" 

顺便说一句,Ruby 1.8中的proc会创build一个lambda,而Ruby 1.9中的proc就像Proc.new一样,这真是令人困惑。

详细阐述手风琴家的回应:

请注意, Proc.new通过传递一个块来创build一个proc。 我相信lambda {...}被parsing为一种文字,而不是通过一个块的方法调用。 从附加到方法调用的块中返回将从方法返回,而不是块返回,而Proc.new事件就是这种情况的一个例子。

(这是1.8,我不知道这个数字是1.9。)

我对此有点迟了,但是关于Proc.new一个很不为人知的事情, Proc.new没有提到。 如文件所示 :

Proc::new只能在带有附加块的方法内调用,而在这种情况下该块将转换为Proc对象。

这就是说, Proc.new让连锁产量的方法:

 def m1 yield 'Finally!' if block_given? end def m2 m1 &Proc.new end m2 { |e| puts e } #⇒ Finally! 

return行为的差异是恕我直言,最重要的差异之二。我也更喜欢拉姆达,因为它比Proc.new打字less:-)