为什么需要匿名函数?
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,将调用在调用这个方法时作为参数传递的块。
在某种程度上,执行流程可以被琐碎地解释成这样。
say_hey方法正常执行。yield将执行调用时传递的代码块(如果你迷失了方向,总是想到 "匿名函数")。正常的执行流程以某种方式被 "暂停",以执行这个匿名函数。- 一旦区块执行完毕,方法主体继续执行。
如何向块传递参数
如果你想把参数传给产量怎么办?想想你是如何向方法传递参数的,比如每次你给它一个块。
[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 也不会中断流程。breakreturnbreaknext在Procs和Lambdas中的行为是一样的。
| 块 | 程序 | 兰姆达 | |
|---|---|---|---|
| 类 | 进程 | Proc | 进程 |
| 将停止执行流程 | 不适用 | 不适用 | 不适用 |
| 可以被分配给变量 | 不适用 | 是 | 是的 |
| args的数量是否重要? | 不重要 | 不重要 | 是的 |
主要启示
现在我们已经介绍了procs和lambdas这两个块,让我们退一步总结一下比较。
-
块在Ruby中被广泛用于向函数传递代码位。通过使用yield关键字,一个块可以被隐式传递,而不需要将其转换为proc。
-
当使用前面有安培尔的参数时,在方法的上下文中,将一个块传递给方法的结果是一个proc。Procs的行为与块类似,但它们可以被存储在一个变量中。
-
Lambdas是行为类似于方法的proc,这意味着它们强制执行arity,并作为方法返回,而不是在其父级范围内返回。