Ruby中的proc和lambda有什么区别?


Answers:


264

一种区别是他们处理参数的方式。使用proc {}和创建procProc.new {}等效。但是,usinglambda {}给您提供了一个过程,可以检查传递给它的参数数量。来自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指出的那样,return在lambda内部使用将返回该lambda的值,而return在proc中使用将从封闭的块返回。

lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return

因此,对于大多数快速使用而言,它们是相同的,但是如果您要进行自动严格参数检查(有时也可以帮助调试),或者如果您需要使用该return语句返回proc的值,请使用lambda


8
准确地说lambda非常类似于方法(检查参数和return将从它们返回),而procs非常类似于块(参数不被检查并且return将从包含方法或lambda返回)吗?
pedz

1
我去过上帝,到目前为止,我知道有多少网站和文章,而且似乎没有人谈论Procs,方法,Lambda的实用性。每个解释都只是详细地说明了返回值等如何不同,而没有一个关于为何如此重要的细节。现在,我必须得出结论,这是Ruby中的设计混乱。
ankush981

77

proc和lambda之间的真正区别与控制流关键字有关。我说的是returnraisebreakredoretry等等-这些控制字。假设您在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方法中的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自身中转储出来,并且封装方法继续执行。在proc和lambda中处理控制流关键字的方式是它们之间的主要区别


9

只有两个主要区别。

  • 首先,alambda检查传递给它的参数数量,而aproc不检查。这意味着,lambda如果您传递错误数量的参数,则a将引发错误,而aproc将忽略意外的参数,并将其分配nil给所有遗漏的参数。
  • 其次,当alambda返回时,它将控制权传递回调用方法。当proc返回时,它将立即执行操作,而无需返回调用方法。

要查看其工作原理,请看下面的代码。我们的第一种方法称为a proc; 第二个叫a 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但是,我们的方法在被调用后返回该方法,因此该方法返回它评估的最后一个代码:“铁人将获胜!”


7

#过程示例

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之间的区别

在介绍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内部的“ 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语句返回proc的值,请使用lambda。

最初发布在这里


1

一般来说,lambda比proc更直观,因为它们与方法更相似。他们对Arity非常严格,并且在您致电return时退出。因此,除非他们需要procs的特定功能,否则许多Rubyist都将lambda作为首选。

过程: class的对象Proc。像块一样,它们在定义它们的范围内进行评估。 Lambdas:也是类的对象,Proc但与常规proc略有不同。它们是像块和proc这样的闭包,因此,它们在定义它们的作用域中进行评估。

创建过程

a = Proc.new { |x| x 2 }

创建lambda

b = lambda { |x| x 2 }


a = proc { |x| x 2 }a = Proc.new { |x| x 2 }
lacostenycoder

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

在上述方法中,&会将传递给该方法的块转换为一个对象,并将该对象存储在局部变量块中。实际上,我们可以证明它具有与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内部执行返回时,它实际上是从封闭方法(即周围的上下文)返回的。从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时,它将退出封闭方法。

您可以将lambda视为匿名方法,它们将各个代码块隔离到一个对象中,该对象可以像方法一样对待。最终,将lambda视为一种行为学方法,而将Proc.new行为视为内联代码。



-3

proc和lambda之间的区别在于proc只是代码的副本,其中的参数依次被替换,而lambda是类似于其他语言的函数。(返回行为,参数检查)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.