打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ruby系列教材(24):Implementing Iterators
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)
from -:4
不过这里说了:ruby解释器,先了解method_name(parameter,parameter)就进入class definition,遇见yield就运行block,所以下面的代码也是合法的:
def test
p "run"    #没有yield,也可以运行
end
test{}
结果:
run
Block可以接收一个来自yield处的变量,也可以返回一个值
1)Block接收值
值(value)来源于yield,例如
def fib_up_to(max)
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]
b = ‘cat‘
a.each{|b| c = b * a[1]}
问题出现了,1)a,b在括号里面出现,会不会改变其值 2)c在括号外面可以用吗?
结果是:
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结束
find 适用于Array,yield带有1个parameter,迭代过程从0-array.length,返回值是element类型(or nil)
each 适用于Array,yield带有1个parameter,迭代过程从0-array.length,返回值是这个array本身
times 适用于Fixnum,yield带有1个parameter,迭代过程从0-(num-1),返回值是这个fixnum
上面的总结不全面,注意,对于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 = num.collect do |i|
if i%2==0
i
else
[]
end
end
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
 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
【Python之路】特别篇
聊聊 Ruby 中的 block, proc 和 lambda
Ruby 块
Json相关功能块
由浅入深表达式树(一)
Ruby学习笔记
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服