Block的内部调用过程:
调用method three_times的时候,遇见yield,就立即调用Block,block运行完毕,马上返回yield的下一句
method_name(parameter,parameter){block} 这里,解释器遇见method_name(parameter,parameter)就进入method_name(parameter,parameter)的定义体,运行,当遇见block的时候,就执行{}中的内容,也就是解释器遇见method_name(parameter,parameter)时候并不关心后面的内容,也就是block的内容,只是在运行遇见yield statement的时候才转到block这里,所以在class里面定义以后,调用时候必须时候,否则将出现错误: def test yield end test
结果: in `test‘: no block given (LocalJumpError)
不过这里说了:ruby解释器,先了解method_name(parameter,parameter)就进入class definition,遇见yield就运行block,所以下面的代码也是合法的: def test p "run" #没有yield,也可以运行 end test{}
结果: run
Block可以接收一个来自yield处的变量,也可以返回一个值 1)Block接收值 值(value)来源于yield,例如 i1,i2 = 1,1 # parallel assignment (i1 = 1 and i2 = 1) while i1 <= max yield i1 #i1讲被传递到block里面 i1,i2 = i2,i1+i2 end end
fib_up_to(1000){|f| print f," "} #i1的值赋值给f,表现就是i1的值传递到了block里面的f了,使用|variable|来接受值
关于block的应用我们很久以前就说过了,假如yield有2个parameters,那么这里要用|para1,para2|这样的形式
前面我们提过,不能从语法的角度来理解ruby,要从语意的角度理解ruby,因为ruby是一门更加贴近问题域的语言 这里yield i1有一层语意就是,我要把 i1 这个值传递给一个block
我们下面看一段程序: a = [1,2]
结果是: a→[1, 2] b→2 #值被括号内改变 defined?(c)→nil #c没有被定义,也就是c出了括号,就没有了
事实上有2条规则: a. block外面的variables出现在block内部,内部将直接改变值 b. block外面的variables没有出现在block内部,这时候的variables作用域仅仅在这个block内或者说是属于这个block
这样我们得到了block和外部环境交互的能力,但是这样的方式也遭到了很多的质疑,也许会改今后的版本中进行一定的调整
*注意,一般的情况,比如find,each,times这些迭代器都是从0开始到max结束
上面的总结不全面,注意,对于each,times我们根本不关注他们的返回类型
2)Block返回值 一个block可以返回一个值,可以认为一个yield(parameter,parameter)可以返回一个值,这个值是block中,最后一次赋值的表达式的值(同于methods)
我们前面提到过find iterator,可能大家会觉得有些迷惑,它的实现如下: class Array def find for i in 0...self.length #self表示引用它的object value = self[i] return value if yield(value) #yield返回一个值,这里返回的是一个true or false end return nil end end
*上面的self.length可以写成 size,表示引用它的对象的大小
self 表示应用这个method的object,例如,在method里面有self,123.method_name 这个时候,self表示123这个object
yield 有什么好处呢?yield实现了代码级的复用,我们一般来说,实现的是method级别的复用,也就是复用方法,而yield提供了这样的能力,使得我们重复出现的代码都消失了,这是十分神奇的
Iterator:(一般来说只要是collection就有他的iterators) 1)each 遍历array中的所有element,对于array来说,可以这样用: [1,2,3,4,5].each{|i| p i}
对于File class each iterator每次从file object里面每次读出一行: f = File.open("testfile") f.each do |line| #一次读出一行 puts line end f.close
2)collect 和each一样进行遍历,但是collect将所有的block的返回值收集起来,建立一个array object返回,例如: num = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] num2.each{|i| print i," "}
结果: 2 4 6 8 10 12 14
3)inject inject可以带parameter,inject的parameter和yield的parameter有一定的关系,yield有2个parameters object.inject(a){|p1,p2| p1+p2} 这个表示p1初始化为a,p1以后的值为block的返回值,p2是object elements的值,一直遍历过去 举例说明: print [1,2,4,9].inject(0){|sum,ele| sum+ele} #结果:16 print [1,2,4,9].inject(1){|sum,ele| sum*ele} #结果:72
inject也可以不带parameter,这个时候,yield第一个parameter的值为array object第一个element的值,yield第2个parameter的值是array object的第2个element的值,比如: print [1,2,4,9].inject{|sum,ele| sum*ele} #结果:72 .............1) print [1,2,4,9].inject{|sum,ele| sum+ele} #结果:16 .............2) 1)中sum初始化的值是1,ele最初值是2 2)中sum初始化的值是1,ele最初值是2
|
|