列表内容
一、什么是ruby中的block?
Block是一个统称,中文名称又叫闭包,英文是Closure,表现形式有block, Proc and lambda。Proc是对block的面向对象的封装, lambda是对Proc的进一步封装。
block书写方式
大括号+代码 { p ‘hi’} do … end,也是代码块
二、block 虽然Ruby中万物皆对象,但block是作为一个特性而存在,不是对象 看如下代码:
> my_array = [ 1, 2, 3, 4, 5 ] my_array.each { | number | puts number} > my_array.each do | number | puts number end my_array.each_index { | > index | puts "number has #{index}" }简单来说,each后面的几种表达就是block,上面的例子就是调用Array对象的block方法。看起来好像很神秘,其实我们也可以为自己的类定义一个block方法。
class MyArray attr_accessor :my_arr def initialize( my_arr ) @my_arr = my_arr end def my_each( &my_block ) for i in 0..@my_arr.length-1 my_block.call( @my_arr[i] ) end end a = MyArray.new( [1,2,3,4] ) a.my_each { | number | puts number }结果很简单: 1,2, 3,4
既然已经说到自定义了,不能这样就结束了,我们可以再复杂一点:
class MyArray attr_accessor :my_arr def initialize( my_arr ) @my_arr = my_arr end def my_each( &my_block ) for i in 0..@my_arr.length-1 my_block.call( @my_arr[i], i ) end end end a = MyArray.new( [ 1, 2, 3, 4 ] ) a.my_each { | number, index | puts "number at #{index} has value #{number}" }结果如下: number at 0 has value 1 number at 1 has value 2 number at 2 has value 3 number at 3 has value 4
接下来不得不说一下yield这个关键字,我们从简单的例子开始
class MyArray attr_accessor :my_arr def initialize( my_arr ) @my_arr = my_arr end def my_yield yield end end a = MyArray.new( [ 1, 2, 3, 4 ] ) a.my_yield { puts "yield is also sexy!" }请大家无视1,2,3,4, 上面的例子只会输出yield is also sexy!, 也就是说 a.my_yield 后面的所有内容都跑到 my_yield 中,替换了 yield,简单吧。
下面开始对其升级:
class MyArray attr_accessor :my_arr def initialize( my_arr ) @my_arr = my_arr end def my_yield yield( @my_arr ) end end a = MyArray.new( [ 1, 2, 3, 4 ] ) a.my_yield {| my_tmp_arr | puts "yield with parameter!" my_tmp_arr.each{| number | puts number} }输出如下: yield with parameter! 1 2 3 4 如果你不是高手,我相信你会回头再品一下代码的,这个my_yield中到底发生了什么事?其实也不难,按照上例中的,把a.my_yield后面的全部甩到my_yield中替换yield, 然后用@my_arr替换my_tmp_arr就可以了。
三、Proc 前面说到Proc是Ruby对block的面向对象的封装,简单来说,就是我自己定义一个可以多次重用的block。还是看个例子吧,比如我想计算一个长方形的面积:
rectangle_area = Proc.new{ | a, b | puts a * b } rectangle_area.call( 5, 6 )如果我的想固定长边,只输入宽度就好了,那我可以加入一个参数:
def rectangle_area_with_length (length) Proc.new{ | width | width * length } end area = rectangle_area_with_length(6) area.call(3) area[3] area.class # => Proc四、lambda lambda其实是Ruby的一个函数,用来创建Proc。
multiply_lambda_proc = lambda { | x, y | x * y } multiply_lambda_proc.call( 3, 4 ) # return 12五、proc和lambda区别 主要有两个不同点:
第一,lambda 会检查参数,而 Proc 不会。
multiply_lambda_proc = lambda { | x, y | x * y } multiply_proc = Proc.new { | x, y | x * y } multiply_lambda_proc.call( 3, 4, 5 ) # ArgumentError: wrong number of arguments (3 for 2) multiply_proc.call( 3, 4, 5 ) # return 12 as normal multiply_proc.call( 3 ) # TypeError: nil can't be coerced into Fixnum第二,lambda 会返回它的调用函数,但 Proc 会结束它所位于的 function。 这句话不太好理解,说白了也是block与lambda的区别,lambda本质上是函数,所以遇到return时会从lambda函数内返回,而proc是一段能执行的代码,相当于你在函数中插入一段断码,当proc中包含return语句时,导致整个函数就返回了
def return_from_proc ruby_proc = Proc.new { return "return from a Proc" } // 等价于 return "return from a Proc" 这里相当于跳出了这个函数 ruby_proc.call return "The function will NOT reach here because a Proc containing a return statement has been called" end def return_from_lambda ruby_lambda = lambda { return "return from lambda" } //等价于def ruby_lambda return "return from lambda" end 这里的return相当于跳出了这个ruby_lambda 函数下面语句还会执行 ruby_lambda.call return "The function will reach here" end puts return_from_proc # display return from proc puts return_from_lambda # display The function will reach here六、块怎样转换成proc对象?
1.Proc.new + 代码块
Proc.new { |imsi| where(imsi: imsi) }2.proc + 代码块(proc缩写)
proc { |imsi| where(imsi: imsi) }3.lambda + 代码块
lambda{ |rs| where.not(report_status: rs) }4.-> + 代码块(lambda缩写)
--> (rs) { where.not(report_status: rs) }总结: block不是对象,就是一个代码块,可以想象成proc的实例对象,只能调用一次 proc的对block进一步封装,可以进行复用 lambda的对proc进一步封装,其实是一个函数,更严谨,会检查参数