zoukankan      html  css  js  c++  java
  • Ruby 实现迭代器

    Ruby的迭代器只不过是可以调用block的方法而已。Ruby的block不是传统意义上的、将语句组织在一起的一种方式。

    首先,block在代码中只和方法调用一起出现:block和方法调用的最后一个参数处于同一行,并紧跟在其后(或者参数列表的右括号的后面)。其次,在遇到block的时候并不立刻执行其中的代码。Ruby会记住block出现时的上下文(局部变量、当前对象等)然后执行方法调用。

    在方法内部,block可以像方法一样被yield语句调用。每执行一次yield,就会调用block中的代码。当block执行结束时,控制返回到紧随yield之后的那条语句。我们来看个简单的例子.

    def three_times
      yield
      yield
      yield
    end
    three_times {puts "Hello"}
    

     输出结果:

    Hello
    Hello
    Hello
    

     block(花括号内的代码)和对方法three_times的调用联合在一起。该方法内部,连续3次调用了yield。每次调用时,都会执行block中的代码,并且打印出一条欢迎信息。更有趣的是,你可以传递参数给block,并获得其返回值。例如,我们可以写个简单的函数返回低于某个值得所有Fibonacci数列项。

    def fib_up_to(max)
      i1,i2=1,1
      while i1<=max
        yield i1
        i1,i2 = i2,i1+i2
      end
    end
    fib_up_to(1000)   {|f| print f," "}
    

     输出结果:

    1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
    

     在这个例子中,yield语句带有一个参数。参数值将传递给相关联的block。在block定义中,参数列表位于两个竖线(管道符)之间。在这个例子中,变量f收到yield的参数的值,所以block能够输出数列中的下一个项(这个例子也展示了并行赋值的用法)。尽管通常block只有一个参数,但这不是必然的;block可以有任意数量的参数。

    如果传递给block的参数是已存在的局部变量,那么这些变量即为block的参数,它们的值可能会因block的执行而改变。同样的规则适用于block内的变量;如果它们第一次出现在block内,那么它们就是block的局部变量。相反,如果它们先出现在Block外,那么Block就与其外部环境共享这些变量。

    下面的例子中,我们看到block从外围环境中继承了变量a和b,而c是block的局部变量(defined?方法在其参数没有定义时返回nil)。

    a = [1,2]
    b ='cat'
    a.each{|b| c=b*a[1]}
    
    a                   ->    [1,2]
    b                   ->    2
    defined?(c)         ->    nil         
    

     block也可以返回值给方法。block内执行的最后一条表达式的值被作为yield的值返回给方法。这也是Array类的find方法的工作方式。它的实现类似于下面的代码。

    class Array
       def find
          for i in 0...size
              value = self[i]
              return value if yield(value)
          end
          return nil
       end
    end
    
    [1,3,5,7,9].find {|v| v*v>30}             ->  7
    

     上面的代码把数组中的元素依次传递给关联的Block。如果block返回真,那么方法返回相应的元素。如果没有元素匹配,方法则返回nil.这个例子展示了这种实现迭代器的方式的优点。数组类处理它擅长的事情。例如访问数组元素,而让应用程序代码集中精力处理特殊需求。

    一些迭代器是Ruby的许多收集(collections)类型所共有的。我们已经看了find方法。另外两个是each和collect。each可能是最简单的迭代器,它所做的就是连续访问收集的所有元素。

    [1,3,5,7,9].each {|i| puts i}
    

     输出结果:

    1
    3
    5
    7
    9
    

     另一个常用的迭代器是collect,它从收集中获得各个元素并传递给block。block返回的结果被用来生成一个新的数组,例如:

    ["H","A","L"].collect {|x| x.succ}     ->["I","B","M"]
    

     (备注:.succ方法:返回str的继承者,继承者由最右边的字母或数字递增一个计算出(如果最右边不是字母或数字,那么用最右边字母或字符),递增一个数字通常得到另一数字,同样递增一个字符得到另外一个字符)

    让我们再来看一个有用的迭代器。inject方法(定义在Enumerable模块中)让你可以便利收集的所有成员以累计出一个值。例如,使用下面的代码你可以将数组中的所有元素加起来,并获得它们的累加和。

    [1,3,5,7].inject(0){|sum,element| sum+element}               ->16
    [1,3,5,7].inject(1){|product,element| product*element}       ->105
    

     inject是这样工作的:block第一次被执行时,sum被置为inject的参数,而element被置为收集的第一个元素。接下来每次执行block时,sum被置为上次block被调用时的返回值。inject的最后结果时最后一次调用block返回的值。还有一个技巧:如果inject没有参数,那么它使用收集的第一个元素作为初始值,并从第二个元素开始迭代。

  • 相关阅读:
    【原创】kafka consumer源代码分析
    【原创】kafka server源代码分析(二)
    【原创】kafka server源代码分析(一)
    【原创】kafka controller源代码分析(二)
    棋盘格检测
    人脸三维建模A Morphable Model For The Synthesis Of 3D Faces(三维人脸合成的变形模型)
    汉字识别关键技术与应用
    城市研究中的常见数据类型及其应用场景
    OpenCV Sift源码分析
    OpenCV feature2d
  • 原文地址:https://www.cnblogs.com/timsheng/p/2600148.html
Copyright © 2011-2022 走看看