ruby中的block是比较难理解的一个概念,但是如果你懂得了什么是回调函数。那么其实block的作用和回调函数是一样的。
我们先看一个没带参数的例子:
def say
yield
yield
end
我们先看单行的block。
say{puts("hello world")}
输出的结果为:
hello world
hello world
block就象是一个没有名称的函数,执行到yield就调用block中的代码执行。
如果block是多行的,那么就应该是这样:
say do
puts("hello world")
puts("ni hao");
end
输出结果:
hello world
ni hao
hello world
ni hao
每一个yield就是执行完block中所有的代码。
接下去我们看看带有参数的block。
def add
puts(yield(1,2))
puts(yield(5,6))
end
add{|a,b| a+b}
输出的结果:
3
11
block中的a,b就是这个block的参数,yield(1,3)就是调用这个block,并把1,3赋给a和b。
Ruby中方法和块的关系就是一对协同例程,互相转换程序的控制执行权,当方法中遇到yield语句,就转到块里执行,块执行完毕后,将返回到方法体中,继续执行方法yield语句后面的代码。yield可以带参数到块中,块也是有返回值,返回到方法体中。
方法和块的一个作用,就是实现了操作和处理的分离,比如:File.open {...},File.open负责打开文件(即是操作),然后呢,如何处理呢,交给了块。Ruby创立者这种思想,深刻地理解了,如何更贴近自然的语 言,更加深入的面向对象,更加的灵活(比如Array.each方法,只是负责取出数组中的每个元素,具体要干什么,块可以自由的设定)。
方法可以通过 if 语句加上 block_given? 条件,来判断方法在执行的时候,是否有块在后面,如果有,就执行if语句,再根据yield调用块,如果后面没有块,就继续执行if后面的代码。
块只是接过方法交过来的执行权,继续执行块的代码,它不是一个结构,不是循环。
下面是个例子,File.open方法后跟块的时候,可以在结束块时,自动关闭文件,完善了File.new方法。
class File
def File.open(*args) #将File.new方法需要的一篮子参数放到*args数组里
r=f=File.new(*args) #将数组*args分散开并传给File.new ,为什么这里要把句柄给两个值?
if block_given? #block_given?方法属于Kernel模块,而此模块包含在Object中,
begin #定义一个begin结构,以便即使有错误,也可以执行ensure语句
r=yiled f # f传递到块中,r接受了块的返回值。
ensure
f.close #不管有没有错误,都会在退出块时,关闭文件,关闭文件,
end #可以确保缓存的东西都写入文件,并且可以交出占用的资源。
end
return r #如果不存在块,直接返回r
end
end