ITPUB??ì3
ITPUB论坛 » 动态语言 » ruby的Implementing Iterators


标题: ruby的Implementing Iterators
离线 四紫
中级会员



精华贴数 0
个人空间 0
技术积分 1244 (1364)
社区积分 0 (1030592)
注册日期 2006-7-15
论坛徽章:0
      
      

发表于 2006-8-18 12:33 
ruby的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

      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


只看该作者    顶部
 
    

相关内容


CopyRight 1999-2006 itpub.net All Right Reserved.
北京皓辰广域网络信息技术有限公司. 版权所有
E-mail:Webmaster@itpub.net
京ICP证:010037号 联系我们 法律顾问