Ruby 的 proc 与 lambda

2017-12-12 12:04:34来源:oschina作者:傅易人点击

分享

在一个作用域里,变量名与一组变量值相绑定,改变了作用域,就可以与另一组变量值相绑定,且不影响原作用域的值。使用 Kernel#local_variables 查看当前作用域下的变量。


开启作用域的地方称为作用域门(scope gate),ruby 里只有三个


类定义:class ... end
模块定义:module ... end
方法定义:def ... end


proc 不开辟新的作用域,当你需要新的作用域怎么办?


使用 lambda 创建“提供了一层新作用域”的 Proc 对象。


p1 = lambda { |x| x+1 }
p1.class # Proc
p2 = -> { }
p2.class # Proc

虽然 lambda 的结果仍然是 Proc 对象,但与通过 block 产生的 Proc 对象不同。


Ruby 1.9 开始,为了强调两者的不同,在 puts 时你可以看到这样的区别:


irb(main):001:0> a = lambda {}
=> #
irb(main):002:0> b = proc {}
=> #

值得注意的是,Ruby 没有 Block 类,没有 Lambda 类,只有 Proc 类,用以代表各种形式的代码块。


可以认为,lambda 产生的是“匿名方法”,而 proc 产生的是纯粹的“匿名代码块”。这种认知能够解释种种 proc 与 lambda 的不同行为。




这一层新的作用域带来了什么样的变化?想想 def 定义方法的时候。


def test_proc
p = proc { return 10 }
return p.call() * 2
end
puts test_proc() # 10
def test_lambda
p = lambda { return 10 }
return p.call() * 2
end
puts test_lambda() # 20

相比于 block 只能由最后一个表达式作为返回值,lambda 可以使用 return 返回结果。而 block 使用 return 相当于在 def 定义的方法的作用域中返回了。


同理,block 中的 break/next 是在 block 外部的作用域中执行,而 lambda 中则是在其内的新作用域中执行。




lambda 产生的匿名方法,与 def 定义的方法一样会严格检查传参,而不似 proc 般宽容。


p1 = proc { |x,y| x+y }
puts p1.call(1) # 1 + nil, TypeError
puts p1.call(1,2) # 3
puts p1.call(1,2,3) # 3
p2 = lambda { |x,y| x+y }
puts p2.call(1) # given 1, expected 2, ArgumentError
puts p2.call(1,2) # 3
puts p2.call(1,2,3) # given 3, expected 2, ArgumentError


从参数检查、返回行为、上下文的绑定三个点出发就能掌握 block 和 lambda 的使用。


正如 聊聊 Ruby 中的 block, proc 和 lambda 中所说:


> 用 block,用 lambda,慎用 proc,让 proc 做好自己的幕后工作。


我觉得,大致可以用这么几句话总结:


block 是 Ruby 的一种字面量(Literal)。
block 以及 Proc 化的 block,它的行为就像宏展开。
lambda 是真正的匿名方法。
Proc 对象是 block 和 lambda 的容器。

相关文章

    无相关信息

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台