在 Ruby 中,blok,proc 和 lambda 有什么区别?

377 阅读4分钟

bloks,procs和lambdas是什么?

Coder Talk:Ruby中_closures_的示例, 原文

Plain old english:我们想要运行的代码分组方法。

# Block Examples

[1,2,3].each { |x| puts x*2 } # blok is in between the curly braces

[1,2,3].each do |x|
  puts x*2 # blok is everything between the do and end
end

# Proc Examples             
p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p) # The '&' tells Ruby to turn the proc into a blok 

proc = Proc.new { puts "Hello World" }
proc.call # The body of the Proc object gets executed when called

# Lambda Examples            
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)

lam = lambda { puts "Hello World" }
lam.call

虽然看起来这些都非常相似,但我将在下面介绍一些细微差别。

Blocks和Procs之间的差异

Procs are objects, blocks are not

proc(注意小写的p)是Proc类的一个实例。

p = Proc.new { puts "Hello World" }

这让我们可以在其上调用方法并将其分配给变量。 Procs也可以自己回归。

p.call # prints 'Hello World'
p.class # returns 'Proc'
a = p # a now equals p, a Proc instance
p # returns a proc object '#<Proc:0x007f96b1a60eb0@(irb):46>'

相比之下,blok只是方法调用的\ * syntax*的一部分。 它并不代表任何独立的东西,只能出现在参数列表中。

{ puts "Hello World"} # syntax error  
a = { puts "Hello World"} # syntax error
[1,2,3].each {|x| puts x*2} # only works as part of the syntax of a method call

  1. At most one block can appear in an argument list

相反,您可以将多个过程传递给方法。

def multiple_procs(proc1, proc2)
  proc1.call
  proc2.call
end

a = Proc.new { puts "First proc" }
b = Proc.new { puts "Second proc" }

multiple_procs(a,b)

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是一种不同的“味道"。 返回对象时会显示这种细微差别。

proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'

(lambda)符号提醒一下,虽然procs和lambdas非常相似,即使是Proc类的两个实例,它们也略有不同。 以下是主要差异。

Lambdas check the number of arguments, while procs do not

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.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

如上所示,如果传递了错误数量的参数,则procs不会出错并引发错误。 如果proc需要参数但没有传递参数,则proc返回nil。 如果传递的参数太多而忽略了额外的参数。

  1. Lambdas and procs treat the ‘return’ keyword differently

lambda中的'return'会在lambda代码之外触发代码

def lambda_test
  lam = lambda { return }
  lam.call
  puts "Hello world"
end

lambda_test yyy188zzzcalling lambda_test prints 'Hello World'

proc中的'return'触发执行proc的方法之外的代码

def proc_test
  proc = Proc.new { return }
  proc.call
  puts "Hello world"
end

proc_test yyy188zzzcalling proc_test prints nothing

什么是封闭?

Coder Talk:'函数或对函数的引用以及引用环境。 与普通函数不同,闭包允许函数访问non-local变量,即使在其直接词法范围之外调用它。' - Wikipedia

Plain old english:类似于一个手提箱,它是一组代码,当打开(即调用)时,包含打包它时所包含的内容(即创建它)。

# Example of Proc objects preserving local context

def counter
  n = 0
  return Proc.new { n+= 1 }
end

a = counter
a.call # returns 1
a.call # returns 2

b = counter
b.call # returns 1

a.call # returns 3

Background第1部分:Lambda微积分和匿名函数

Lambda的名字源于20世纪30年代引入的一种微积分,以帮助研究数学的基础。 Lambda演算通过简化其语义,有助于使可计算函数更容易学习。 这些简化中最相关的是“匿名"处理函数,这意味着没有给函数赋予明确的名称。

sqsum(x,y) = x*x + y*y #<-- normal function
(x,y) -> x*x + y*y #<-- anonymous function

一般来说,在编程中,术语lambda指的是匿名函数。 这些匿名函数在某些语言(即Javascript)中是非常常见和明确的,而在其他语言中是隐含的(即Ruby)。

Background第2部分:名称过程来自何处

Proc是程序的简称,程序是一组打包作为执行特定任务的单元的指令。 在不同的语言中,这些可以称为函数,例程,方法或通用术语可调用单元。 它们通常被多次调用,并在程序中多次调用。

Summary差异

Procs are objects, blocks are not 2. At most one block can appear in an argument list Lambdas check the number of arguments, while procs do not Lambdas and procs treat the ‘return’ keyword differently