何时使用lambda,何时使用Proc.new?
在Ruby 1.8中,proc / lambda和Proc.new
之间有细微差别。
- 这些差异是什么?
- 你可以给如何决定哪一个select的指导方针?
- 在Ruby 1.9中,proc和lambda是不同的。 这是怎么回事?
使用Proc.new
创build的使用lambda
和Proc.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打破,因为它本质上是一个方法,没有任何意义,你永远不会从一个方法的顶层破坏。
next
, redo
和raise
在Procs和lambdas中performance相同。 而任何一方都不允许retry
,并会引发例外。
最后, proc
方法不应该被使用,因为它是不一致的,并且具有意想不到的行为。 在Ruby 1.8中它实际上返回一个lambda! 在Ruby 1.9中,这个问题已经修复,并且返回一个Proc。 如果你想创build一个Proc,坚持Proc.new
。
欲了解更多信息,我强烈build议O'Reilly的Ruby编程语言 ,这是我的大部分信息的来源。
我发现这个页面显示了Proc.new
和lambda
之间的区别。 根据页面,唯一的区别是一个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在我学习这门语言的时候)是因为:
- 如果你使用proc,你很可能使用某种function范例。
- 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:-)