那么,在何时使用其中一种而不是另一种呢?
其中一个区别在于它们处理参数的方式不同。使用proc {}
和Proc.new {}
创建一个proc是等效的。然而,使用lambda {}
会给你一个检查传递给它的参数数量的proc。来自ri Kernel#lambda
:
与
Proc.new
相同,但返回的Proc对象在调用时检查传递的参数数量。
一个例子:
p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)
另外,正如Ken指出的那样,在lambda中使用return
将返回该lambda的值,但在proc中使用return
则会从封闭块中返回。
lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return
因此,对于大多数快速使用情况,它们是相同的,但如果您想要自动严格的参数检查(这有时也可以帮助调试),或者如果您需要使用 return
语句来返回 proc 的值,请使用 lambda
。
Proc和Lambda之间的真正区别与控制流关键字有关。我说的是return
、raise
、break
、redo
、retry
等控制词。假设您在一个proc中有一个return
语句。当您调用这个proc时,它不仅会退出该proc,还将从封闭方法返回,例如:
def my_method
puts "before proc"
my_proc = Proc.new do
puts "inside proc"
return
end
my_proc.call
puts "after proc"
end
my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
由于我们在调用proc时,其中的return
语句将我们跳出方法,所以方法中的最后一个puts
语句从未执行。但是,如果我们将我们的proc转换成lambda表达式,我们会得到以下结果:
def my_method
puts "before proc"
my_proc = lambda do
puts "inside proc"
return
end
my_proc.call
puts "after proc"
end
my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc
lambda 内的返回语句只会退出 lambda 本身,而封闭的方法将继续执行。控制流关键字在 procs 和 lambdas 中的处理方式是它们之间的主要区别。
只有两个主要的区别。
lambda
检查传递给它的参数数量,而 proc
不会。这意味着,如果您向 lambda
传递错误数量的参数,它将抛出错误,而 proc
将忽略意外的参数并将 nil
分配给缺少的参数。lambda
返回时,它将控制权传递回调用方法;而当 proc
返回时,它立即返回,不返回到调用方法。为了看看它是如何工作的,请参阅下面的代码。第一个方法调用一个 proc
;第二个方法调用一个 lambda
。
def batman_ironman_proc
victor = Proc.new { return "Batman will win!" }
victor.call
"Iron Man will win!"
end
puts batman_ironman_proc # prints "Batman will win!"
def batman_ironman_lambda
victor = lambda { return "Batman will win!" }
victor.call
"Iron Man will win!"
end
puts batman_ironman_lambda # prints "Iron Man will win!"
看看proc
如何说“Batman will win!”,这是因为它立即返回,而不会回到batman_ironman_proc方法。
然而,我们的lambda
被调用后会回到方法中,所以该方法将返回它评估的最后一行代码:“Iron Man will win!”
# Proc 示例
p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p) # The '&' tells ruby to turn the proc into a block
proc = Proc.new { puts "Hello World" }
proc.call
# Lambda实例
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)
lam = lambda { puts "Hello World" }
lam.call
Proc 和 Lambda 之间的区别
在我深入讲解 Proc 和 Lambda 之间的区别之前,需要提到它们都是 Proc 对象。
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'
然而,Lambda是Proc的不同“风味”。当返回对象时,这种微小的区别得以展现。
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
1. Lambdas 检查参数数量,而 procs 不检查
lam = lambda { |x| puts x } # creates a lambda that takes 1 argument
lam.call(2) # prints out 2
lam.call # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3) # ArgumentError: wrong number of arguments (3 for 1)
相比之下,procs不关心它们是否被传递了错误数量的参数。
proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2) # prints out 2
proc.call # returns nil
proc.call(1,2,3) # prints out 1 and forgets about the extra arguments
2. Lambdas和procs在处理'return'关键字时有所不同
在lambda内部使用'return'会触发lambda代码外部的代码执行。
def lambda_test
lam = lambda { return }
lam.call
puts "Hello world"
end
lambda_test # calling lambda_test prints 'Hello World'
'return'在proc内触发执行该proc的方法外的代码
def proc_test
proc = Proc.new { return }
proc.call
puts "Hello world"
end
proc_test # calling proc_test prints nothing
回答你的另一个问题,何时使用哪个?我会依照 @jtbandes 的建议。
对于大多数快速使用场景它们是相同的,但如果你需要自动严格参数检查(这有时也可以帮助调试),或者你需要使用 return 语句来返回 proc 的值,则使用 lambda。
原始帖子在这里
a = Proc.new { |x| x 2 }
创建lambda函数
b = lambda { |x| x*2 }
a = proc { |x| x 2 }
is the same as a = Proc.new { |x| x 2 }
- lacostenycoder这里有另一种理解方式。
一个块是附加到对象方法调用的调用中的代码块。在下面的示例中,self是匿名类的实例,该类继承自Rails框架(它本身包含许多助手模块)中的ActionView::Base。card是我们在self上调用的方法。我们向该方法传递一个参数,然后我们总是将块附加到方法调用的末尾:
self.card :contacts do |c|
// a chunk of valid ruby code
end
好的,我们现在需要将一段代码传递给一个方法。但是我们如何使用这个代码块呢?其中一种选择是将这个代码块转换成一个对象。Ruby提供了三种将代码块转换成对象的方式。
# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2
# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2
# & as the last method argument with a local variable name
def add(&block)
end
在上述方法中,& 将传递给方法的代码块转换为一个对象,并将该对象存储在本地变量 block 中。实际上,我们可以展示它具有与 lambda 和 Proc.new 相同的行为:
def add(&block)
block
end
l3 = add { |a| a + 1 }
l3.call(1)
=> 2
这很重要。当你将一个块传递给一个方法并使用&进行转换时,所创建的对象会使用Proc.new来进行转换。
请注意,我避免使用"proc"作为选项。因为在Ruby 1.8中,它与lambda相同,在Ruby 1.9中,它与Proc.new相同,在所有Ruby版本中都应该避免使用。
那么你可能会问lambda和Proc.new之间有什么区别?
首先,在参数传递方面,lambda的行为类似于方法调用。如果您传递了错误数量的参数,它将引发异常。相反,Proc.new的行为类似于并行赋值。所有未使用的参数都会转换为nil:
> l = lambda {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb47e40@(irb):19 (lambda)>
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)
> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21>
> l2.call(1)
1 +
其次,lambda和Proc.new在处理return关键字时有所不同。当你在Proc.new内部使用return时,它实际上是从封闭的方法(即周围的上下文)返回。当你从lambda块中返回时,它只是从块中返回,而不是从封闭的方法返回。基本上,它退出了对块的调用,并继续执行封闭方法的其余部分。
> def add(a,b)
l = Proc.new { return a + b}
l.call
puts "now exiting method"
end
> add(1,1)
=> 2 # NOTICE it never prints the message "now exiting method"
> def add(a,b)
l = lambda { return a + b }
l.call
puts "now exiting method"
end
> add(1,1)
=> now exiting method # NOTICE this time it prints the message "now exiting method"
那么为什么会有这种行为差异呢?原因在于使用 Proc.new,我们可以在封闭方法的上下文中使用迭代器并得出逻辑结论。看下面这个例子:
> def print(max)
[1,2,3,4,5].each do |val|
puts val
return if val > max
end
end
> print(3)
1
2
3
4
一篇关于 Ruby 指南的有用文章:块、Proc 和 Lambda
Proc 从当前方法返回,而 Lambda 从 Lambda 本身返回。
Proc 不关心正确数量的参数,而 Lambda 会引发异常。
Proc和Lambda之间的区别在于,Proc只是一个代码副本,并按顺序替换参数,而Lambda则像其他语言中的函数一样。(返回行为,参数检查)
proc
和lambda
中,return
语句返回的结果也有所不同。 - Ken Bloom