学习Ruby block的procs和lambda

126 阅读5分钟

为什么需要匿名函数?

Ruby是一种使用多种编程范式的语言,最常见的是面向对象和函数式,而其函数式的本质就是函数的概念。Ruby使用三种类型的闭包:block、procs和lambdas。

Ruby block:为什么?

Rubyblock是一些匿名的小函数,可以被传递到方法中。它们被包含在do/end语句中(通常当块有多行时)或在大括号{} (如果块是单行的),并且它们可能有多个参数。

关于Ruby中的block,有几件重要的事情。

  • 块可以接受参数并返回一个值。
  • 区块没有自己的名字。
  • 块是由几段代码组成的。
  • 一个块总是与一个函数一起被调用,或者可以被传递给一个方法调用。
  • 为了在方法中用一个值来调用一个块,可以使用yield语句。
  • 块可以作为方法从它们所传递的方法中被调用。

如何定义一个Ruby block

这里我们定义了block_method方法。下面是一个方法的调用,之后我们传递了一个块。你能猜到输出吗?

def say_something
 puts "we are in a method"
end
say_something { "The block is called" }

是的。"我们在一个方法中"。最后一行是对say_something 方法的调用。

但是我们从来没有告诉say_something 读取这个块。

如何用Ruby渲染一个块

下面的代码中到底发生了什么?

def say_hey
  puts "I am inside the method"
  yield
  puts "I am ending the method"
end
say_hey { puts "This is inside the block" }

这一次,如果你调用say_hey方法(没有任何参数,像最后一行那样),将打印出以下内容。

# => "I am inside the method"
# => "This is inside the block"
# => "I am ending the method"

我们首先创建了一个名为say_hey 的方法。然后在下一行,我们打印出 "我在方法里面 "这个字符串。在下一行中,注意我们使用了yield 关键字,它将找到并调用该方法所调用的块。

在一个方法中调用yield ,将调用在调用这个方法时作为参数传递的块。

在某种程度上,执行流程可以被琐碎地解释成这样。

  1. say_hey 方法正常执行。
  2. yield 将执行调用时传递的代码块(如果你迷失了方向,总是想到 "匿名函数")。正常的执行流程以某种方式被 "暂停",以执行这个匿名函数。
  3. 一旦区块执行完毕,方法主体继续执行。

如何向块传递参数

如果你想把参数传给产量怎么办?想想你是如何向方法传递参数的,比如每次你给它一个块。

[1, 2, 3].each {|x| puts x*2 }
# => 2
# => 4
# => 6

在这种情况下,.each 方法需要一个有一个参数的块。

用我们定义的块来做这件事如何?让我们看看yield 是如何接受参数的。

def say_also
  yield 2
  yield 3
end
say_also {|e| puts "e is #{e}"}
# => e is 2
# => e is 3

这里,yield 将调用与方法调用一起传递的块。

在我们的例子中,由于该块需要一个参数,所以给该块一个参数。

第一轮将调用带有参数2的块。控件恢复了方法,然后它将再次调用块,这一次的参数是3。

块:如果调用函数也有参数怎么办?

让我们看一个例子。

def say_again(a, b)
  puts "a is #{a}"
  yield 2
  puts "b is #{b}"
  yield 3
end
say_again("foo", "bar") {|e| puts "e is #{e}"}
# => a is foo
# => e is 2
# => b is bar
# => e is 3

从我们上面看到的情况来看,这里应该没有什么奇怪的。

什么是Procs?

如果你想向你的函数传递两个块怎么办?你如何将你的块保存在一个变量中?

Ruby 引入了procs,这样我们就可以传递块。程序对象是被绑定到一组局部变量的代码块。一旦绑定,代码可以在不同的环境下被调用,并且仍然可以访问这些变量。

如何定义Proc

你可以在Proc 类上调用new来创建一个proc 。你可以使用proc 核心对象。proc 方法只是Proc.new 的别名。这可以被分配到一个变量中。并用.call 方法调用。

让我们看一个例子。

my_proc = Proc.new {|name| puts "Hello #{name}" }
def hello_world(named_proc)
  named_proc.call('Jane')
end
hello_world(my_proc)
# => Hello Jane

什么是Lambdas?

在Ruby中,lambda 关键字被用来创建一个lambda函数。它需要一个块,可以设置零个或多个参数。 你可以使用call方法来调用产生的lambda函数。

例子 :

lamb_one = lambda {|n| "Lambda one was called with #{n}"}
lamb_two = -> (n) { puts "Lambda two with #{n}" }
lamb_one.call("foo")
lamb_two.call("bar")
# => Lambda one was called with foo
# => Lambda two with bar

Procs和Lambdas之间有什么区别。

  • Procs 不关心参数数量是否正确,而 lambdas 会抛出一个异常。
  • return 和 在procs和lambdas中的行为是不同的。即使遇到 或 ,Lambdas 也不会中断流程。break return break
  • next 在Procs和Lambdas中的行为是一样的。
程序兰姆达
进程Proc进程
将停止执行流程不适用不适用不适用
可以被分配给变量不适用是的
args的数量是否重要?不重要不重要是的

主要启示

现在我们已经介绍了procs和lambdas这两个块,让我们退一步总结一下比较。

  • 块在Ruby中被广泛用于向函数传递代码位。通过使用yield关键字,一个块可以被隐式传递,而不需要将其转换为proc。

  • 当使用前面有安培尔的参数时,在方法的上下文中,将一个块传递给方法的结果是一个proc。Procs的行为与块类似,但它们可以被存储在一个变量中。

  • Lambdas是行为类似于方法的proc,这意味着它们强制执行arity,并作为方法返回,而不是在其父级范围内返回。