你何时会使用一个而不是另一个?
答案 0 :(得分:249)
一个区别在于他们处理争论的方式。使用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
。
答案 1 :(得分:69)
procs和lambdas之间的真正区别与控制流关键字有关。我在谈论return
,raise
,break
,redo
,retry
等 - 这些控制词。让我们说你在proc中有一个return语句。当你打电话给你的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
方法中的最后puts
从未被执行过,因为当我们调用proc时,其中的return
将我们排除在方法之外。但是,如果我们将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中处理控制流关键字的方式是它们之间的主要区别
答案 2 :(得分:5)
只有两个主要区别。
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_ironman_proc方法。
但是,我们的lambda
在被调用后返回该方法,因此该方法返回它评估的最后一个代码:“钢铁侠将获胜!”
答案 3 :(得分:2)
#过程示例
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
Procs与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。 Lambda检查参数数量,而proc不检查参数
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)
相反,proc不在乎是否传递了错误数量的参数。
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。 Lambda和proc对“ return”关键字的处理方式不同
在lambda内部“返回”会在lambda代码外部触发代码。
def lambda_test
lam = lambda { return }
lam.call
puts "Hello world"
end
lambda_test # calling lambda_test prints 'Hello World'
proc内部的“ return”会触发执行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。
最初发布的here
答案 4 :(得分:1)
一般来说,lambdas比procs更直观,因为它们是 更类似于方法。他们对arity非常严格,而且他们很简单 当你打电话回来时退出。出于这个原因,许多Rubyist使用lambdas作为 第一选择,除非他们需要触发器的特定功能。
Procs:类Proc
的对象。与块一样,它们在范围内进行评估
他们被定义的地方。
Lambdas:也是类Proc
的对象,但与常规触发略有不同。
它们像块和触发器一样闭合,因此它们被评估
他们定义的范围。
创建过程
a = Proc.new { |x| x 2 }
创建lambda
b = lambda { |x| x 2
}
答案 5 :(得分:1)
这是另一种理解这一点的方法。
块是一个代码块,附加到对对象的方法调用的调用。在下面的示例中,self是一个匿名类的实例,它继承自Rails框架中的ActionView :: Base(它本身包含许多帮助程序模块)。卡是我们称之为自我的方法。我们将一个参数传递给该方法,然后我们总是将该块附加到方法调用的末尾:
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
在上面的方法中,&amp;将传递给方法的块转换为对象,并将该对象存储在局部变量块中。事实上,我们可以证明它与lambda和Proc.new具有相同的行为:
def add(&block)
block
end
l3 = add { |a| a + 1 }
l3.call(1)
=> 2
这是重要的。将块传递给方法并使用&amp;转换它时,它创建的对象使用Proc.new进行转换。
请注意,我避免使用&#34; proc&#34;作为一种选择。这是因为它是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中返回时,它实际上从封闭方法返回,即周围的上下文。从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
我们希望当我们在迭代器中调用return时,它将从封闭方法返回。请记住传递给迭代器的块使用Proc.new转换为对象,这就是为什么当我们使用return时,它将退出封闭方法。
您可以将lambdas视为匿名方法,它们将单个代码块隔离到可以像方法一样对待的对象中。最后,将lambda视为一种动态方法,将Proc.new视为内联代码。
答案 6 :(得分:0)
关于红宝石指南的有用文章:blocks, procs & lambdas
Procs从当前方法返回,而lambdas从lambda本身返回。
过程不关心参数的正确数目,而lambda会引发异常。
答案 7 :(得分:-3)