块
*块(block)*,有时也称代码块,能与参数一起传递的多个处理的集合。像之前的each*,time*就是带块的方法。
1 | (1..5).each do |i| |
上面代码中do和end之间的部分就是所谓的块。这样的方法称为“带块的方法”或“调用块”,其调用方式如下:
对象.方法名(参数列表) do |变量|
希望的处理
end
或者
对象.方法名(参数列表) { |变量|
希望的处理
}
each方法可以将数组的元素一个一个取出来然后处理,散列也可以,但与数组不同的是,散列会将*[key,value]的组合作为数组来提取元素。
1 | outcome = {"breakfast"=>10.00, "lunch"=>20.00, "dinner"=>15.00 } |
在接收块变量时,多重赋值规则也同样适用。
1 | outcome.each { |key, value| |
块除了应用于迭代器以外,还被广泛用于其他地方,其中一个就是确保后处理被执行。看代码
- (1)未使用块做后处理
file_open.rb 1
2
3
4
5file = File.open("each_hash_data.rb")
file.each_line do |line|
puts line
end
file.close - (2)使用块做后处理仔细观察代码,你会发现在第种方式中,File#open方法接收了块,然后将file对象作为块变量,并且执行完毕后,并没有调用close方法去关闭文件。用这种方式会使程序出问题吗?答案是不会,因为块内部其实执行了如下代码的处理。
block_file_open.rb 1
2
3
4
5File.open("each_hash_data.rb") do |file|
file.each_line do |line|
puts line
end
end
1 | file = File.open("each_hash_data.rb") |
块还有一种常见的用法,用于替换部分算法,以Array类的sort方法为例
1 | ary = ["ruby", "pathon", "c#", "javascript"] |
将排序算法作为块传递给sort方法,然后返回按指定排序方式排序后的数组。
注意:块中最后一个表达式的值就是块的执行结果
定义带块的方法
使用yield关键字代替块,如下
1 | def my_block |
传递块参数,获取块的值
1 | def total(from, to) |
**block_given?**方法被用于判断当调用方法时是否有块被传递,有则返回true,否则false
yield参数的个数可以与块变量的个数不一样,当块变量的个数多时,多出的参数会被赋值为nil,当块变量不足时,则不能接受多余的参数值。
*注:break和next方法可指定参数,如果没有指定任何参数,则返回nil,如果指定了参数则返回参数,如:break 0 和next 0 都会返回0。
将块封装为对象
Ruby还可以将块当作对象处理。把块当作对象处理后,就可以在接收块的方法之外的其他地方执行块,或者把块交给其他方法执行。如果要实现这样的功能,就需要Proc对象,Proc对象是能让块作为对象在程序中使用的类。
通过调用Proc.new这个带块的方法,可以定义Proc对象,然后再调用Proc对象的call方法执行块。
1 | hello = Proc.new do |name| # 使用Proc.new方法定义块对象hello |
在方法定义时,如果末尾的参数使用**&参数名**的形式,Ruby就会自动把调用方法时传进来的块封装为Proc对象。
1 | def total(from, to, &block) |
在变量名前添加**&的参数被成为Proc参数**. 而且Proc参数一定要在所有参数之后,也就是方法的最后一个参数。
将Proc对象作为块传递给其他方法处理,在调用方法时,用**&Proc对象**的形式定义参数就可以了。
1 | def my_each(ary, &block) |