多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 代码块 一个块包含的代码块。你可以分配一个名称,一个块。块中的代码总是被括在大括号里({})或是do...end里。 ~~~ [1, 2, 3].each do |i| puts i end #=> 1 2 3 ~~~ 上面这个例子, each方法后面加一个do...end结构,那就是一个块。 Ruby中任何一个方法你都可以传递一个块。 ~~~ def test;end test{ puts i} ~~~ ~~~ def test yield end test{puts "hello test!"} def test(x) yield(x) end test('world!'){|x| puts "hello #{x}"} ~~~ yield关键字不仅可以挂载块(block)代码,而且可以给块传递参数。 ~~~ def test(&block) block.call("world") end test{|msg| puts "hello #{msg}"} ~~~ block到了方法内部,已经被&转化为了一个Proc对象。 ~~~ def test(&block) inner_test(&block) end def inner_test yield("haha!") end test{|msg| puts "hello #{msg}"} ~~~ test方法传进去的block被转化为了Proc对象,而其内部的inner_test又利用「&」把这个Proc对象转化为了块(block) ### 作用域 在Ruby中,关键字class、moduel、def都有自己的作用域范围。 ~~~ class People father = 'God' def my_father puts father end end module Faith def my_father father = 'My God' puts father end end ~~~ 然后我们创建一个对象: ~~~ person = People.new person.my_father #=> NameError: undefined local variable or method `father' for #<People:0x00000003248990> ~~~ 我们看到,报错了,因为作用域的问题,在my_father方法中,找不到这个father的变量。 ~~~ person.extend Faith person.my_father #=> "My God" ~~~ 我们把Faith模块extend到person对象之后,就可以调用my_father方法,这是因为在模块Faith中定义了father变量。两个father变量明显不同。 ### 穿透作用域的块 块(block)有个功能,就是可以穿透上面所说的作用域。 ~~~ class People father = 'The God' define_method :priest do puts "I can talk with #{father}" end end person = People.new person.priest #=> "I can talk with The God" ~~~ 上面代码中, define_method是可以动态的定义一个方法,使用define_method方法的主要原因是想使用块,因为它后面可以加一个块, 也就是 do ... end中包括的内容。我们可以看到上面块中的代码, 直接使用了Class作用域的father变量,并且成功的输出了结果。 这就证明了block有穿透作用域的能力。 ### lambda 和 proc 我们在前面展示了一些block的例子。 我们说Ruby一切皆对象,但是这个block,确不是对象, 不过也不影响那句话,因为block是无法单独存在的,它必须要依靠于一个方法。如果你想让一个block单独被调用,那么就需要把块变成一个Proc对象。 ~~~ lambda = ->(x, y){x * y} #=> #<Proc:0x00000002e593c0@(pry):35 (lambda)> lambda.call(2,3) #=> 6 #也可以省略call,但不可以省略点「.」 lambda.(2,3) #=> 6 ~~~ lambda,是Proc对象的一种类型。它是一个可以被call的对象。 ~~~ proc = proc{|x, y| x * y} #=> #<Proc:0x00000002d1ee38@(pry):38> proc.call(2, 3) #=> 6 proc.(2, 3) #=> 6 ~~~ proc也是一个Proc对象, 注意看lambda和proc生成的Proc对象的差别。 具体的差别可以查看我的blog文章:[大话Rubyblock: http://tao.logdown.com/posts/166766-vernacular-ruby-block](http://tao.logdown.com/posts/166766-vernacular-ruby-block) ### 结语 在Chef中, block的应用是非常常见的, 比如我们随便写个cookbook,都必须得用到, 下面的例子来自于 [chef-server的cookbook](https://github.com/opscode-cookbooks/chef-server/blob/master/recipes/default.rb): ~~~ # Ensure :file_cache_path exists directory Chef::Config[:file_cache_path] do owner 'root' group 'root' recursive true action :create end ~~~ 现在你看懂这样的代码了吗?