ruby的block


Block是一种可以和方法调用相关联的代码块。

Block的代码放置在花括号或者do..end之间,一般单行block用花括号,多行block用do..end:

{ puts 'hello' }
do
  #..sth
end

将block放在方法调用处的后面,就可以实现关联,例如:

def hello
  puts 'hello, 1'
end

hello { puts 'hello, 2' }   #输出hello, 1。没有输出block里的内容是因为hello方法只是关联了block,但并未调用block

def hello
  puts 'hello, 1'
  yield
  yield
end

hello { puts 'hello, 2' }   #输出hello, 1和两次hello, 2。yield语句调用了方法相关联的block

def hello
  yield('levin')
end

hello { |name| puts name }  #竖线称为管道符,管道符中间为参数,多个参数之间可以用逗号隔开

迭代器

先举个例子:

[1, 2, 'a', 'b'].each { |ele| puts ele }

上面是Array的each迭代器,在each方法内部将每个元素作为参数传给相关联的block。

更多的例子:

(3..5).each { |i| puts i }      #输出3 4 5
['a', 'b', 1, 2].each { |ele| puts ele.succ }     #输出b c 2 3,其中succ方法返回某字符的下一位

事务block

block一般和迭代器一起使用,但是block还有别的用处。block可以用来包含必须运行在事务控制环境下的代码。

例如打开一个文件,写入,再关闭此文件,但是我们觉得文件应该自己打开自己,再自己来关闭自己,即让文件管理自己的生命周期。这时候block就派上用场了:

def open_and_process(*args)
  f = File.open(*args)
  yield f
  f.close
end

open_and_process('test_file') do |f|
  while line = f.gets
    puts line
  end
end

在上面,文件首先在open_and_process方法中被打开,然后yield调用block并将文件对象作为参数传递过去,block执行完毕返回后再关闭文件。 通过这种方式,打开和关闭文件的责任从使用者身上转移到了文件本身。

其实ruby的File类里的类方法open支持这样的形式。当open方法和一个block关联时,open会自动执行打开和关闭文件的操作,并调用block。 单独使用open方法时,open将直接返回文件对象,此时文件关闭操作需要使用者自己进行。

一个方法如何判断自己有关联的block?block_given?方法可以判断是否有block和某方法关联,下面是改进的open_and_process方法:

def open_and_process(*args)
  ret = f = File.open(*args)
  if block_given?
    ret = yield f
    f.close
  end
  ret
end

#关联block
open_and_process('test_file') do |f|
  while line = f.gets
    puts line
  end
end

#单独调用
f = open_and_process('test_file')
while line = f.gets
  puts line
end
f.close

整理于Programming Ruby中文版(第2版)。 block在此书中介绍的比较零散,于是总结了下,也加入了自己的领悟。

Published

Author

levin

Category

Program language

Tags

ruby
Disqus loading now...