什么时候你会使用其中一种而不是另一种?
一个不同之处在于它们处理参数的方式。使用proc{}和proc .new{}创建proc是等价的。但是,使用lambda{}提供了一个检查传递给它的参数数量的过程。从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。
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
final函数放入方法中,从未执行过,因为当我们调用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中处理控制流关键字的方式是它们之间的主要区别
一般来说,lambda比procs更直观,因为它们 更类似于方法。他们对arity要求很严格,他们只是 当你调用return时退出。由于这个原因,许多ruby开发者使用lambdas作为 首选,除非他们需要procs的特定功能。
Procs: Proc类的对象。像块一样,它们在作用域中进行计算 它们被定义的地方。 Lambdas:同样是Proc类的对象,但与常规procs略有不同。 它们像block和procs一样是闭包,因此它们被求值 定义它们的范围。
创建过程
a = Proc.new { |x| x 2 }
创建λ
B = lambda {|x| x 2}
这里有另一种理解方式。
块是附加到对象上的方法调用的调用的代码块。在下面的例子中,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
在上面的方法中,&将传递给方法的块转换为一个对象,并将该对象存储在局部变量块中。事实上,我们可以证明它具有与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版本中都应该避免它。
然后你会问,和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视为内联代码。
只有两个主要的区别。
首先,lambda检查传递给它的参数数量,而proc则不检查。这意味着如果传递错误数量的参数,lambda将抛出一个错误,而proc将忽略意外的参数,并将nil分配给任何缺失的参数。 其次,当lambda返回时,它将控制权传递回调用方法;当一个进程返回时,它立即返回,而不返回到调用方法。
要了解这是如何工作的,请查看下面的代码。第一个方法调用一个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!”
一篇关于ruby指南:blocks, procs和lambdas的有用文章
Procs从当前方法返回,而lambdas从lambda本身返回。 Procs不关心参数的正确数量,而lambdas则会引发异常。
证明一下
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和Lambdas之间的区别
在讨论procs和lambdas之间的区别之前,有必要提到它们都是Proc对象。
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'
然而,lambdas是procs的另一种“风格”。这个细微的差别在返回对象时显示出来。
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'
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语句返回 的值,使用lambda。
原贴于此
推荐文章
- 如何记录Ruby代码?
- Ruby:包含的反义词是什么?Ruby数组?
- 在Ruby中,proc和lambda有什么区别?
- 如何传递参数到一个Rake任务与环境在Rails?
- 获取当前正在执行的方法的名称
- 如何在Rails中计算相对时间?
- 在Ruby中使用范围填充数组的正确方法
- “for”和“each”在Ruby中
- 我如何复制一个哈希在Ruby?
- Ruby/Rails:将Date转换为UNIX时间戳
- 我如何编码/解码HTML实体在Ruby?
- Ruby有一个string. startwith ("abc")内置方法吗?
- 如何从URL下载文件并保存在Rails中?
- 从字符串中移除子字符串
- 如何从Ruby数组中创建平均值?