列表内容
一、什么是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进一步封装,其实是一个函数,更严谨,会检查参数