Ruby元编程读书笔记十
&操作符
块就像是方法的匿名参数,绝大多数情况下,在方法中可以通过 yield 语句直接运行一个块。但在下面两种情况中, yield 将力不从心。
-
想把这个块传递给另外一个方法。
-
想把这个块转换为一个 Proc。
下面是把一个块传给另一个方法的例子:
def math(a, b)
yield(a, b)
end
def teach_math(a, b, &operation)
puts "Let's do the math:"
puts math(a, b, &operation)
end
teach_math(2, 3) {|x, y| x * y}
#=> Let's do the math:
#=> 6
如果想把这个块转换为一个 Proc 呢?实际上,如果在上面的代码中引用了 operation,就已经拥有了一个 Proc 对象。& 操作符的真正含义:这是一个 Proc 对象,我想把它当成一个块来使用。简单地去掉 & 操作符,就能再次得到一个 Proc 对象:
def my_method(&the_proc)
the_proc
end
p = my_method {|name| "Hello, #{name}!"}
puts p.class
puts p.call("Bill")
#=> Proc
#=> Hello, Bill!
可以再次使用 & 操作符把 Proc 转换为块:
def my_method(greeting)
puts "#{greeting}, #{yield}!"
end
my_proc = proc {"Bill"}
my_method("Hello", &my_proc)
#=> Hello, Bill!
当调用 my_method() 方法时, & 操作符会把 my_proc 转换为块,再把这个块传给这个方法。
Procs vs. Lambdas
在 proc 和 lambda 之间有两个重要的区别。第一个区别与 return 关键字相关,另一个区别则与参数检验相关。
- 在 lambda 中, return 仅仅表示 从这个 lambda 中返回:
def double(callable_object) callable_object.call * 2 end l = lambda {return 10} double(l) #=> 20 - 在 proc 中, return 的行为则有所不同,它不是从 proc 中返回,而是 从定义 proc 的作用域中返回:
def another_double p = Proc.new {return 10} result = p.call return result * 2 #=> unreachable code! end another_double #=> 10
prco 和 lambda 第二个区别来自它们检查参数的方式。lambda 的适应能力比 proc 差,如果调用 lambda 时的参数数量不对,则它会失败,同时会抛出一个 ArgumentError 错误。而 proc 则会把传递进来的参数调整为自己期望的参数形式:如果参数比期望的要多,那么 proc 会忽略多余的参数,如果参数数量不足,那么对未指定的参数, proc 会赋予一个 nil 值。
- 整体而言,lambda 更直观,因为它更像是一个方法。它不仅对参数数量要求严格,而且在调用 return 时只从代码中返回。
简洁 lambda
简洁 lambda 操作符:
p = ->(x) {x + 1}
上面的代码与下面的代码作用相同:
p = lambda {|x| x + 1}