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没有参数,那么它使用收集的第一个元素作为初始值,并从第二个元素开始迭代。

  • 相关阅读:
    web服务器-Apache
    nginx优化
    nginx下载限速
    nginx-URL重写
    HDU 5358 First One 求和(序列求和,优化)
    HDU 5360 Hiking 登山 (优先队列,排序)
    HDU 5353 Average 糖果分配(模拟,图)
    UVALive 4128 Steam Roller 蒸汽式压路机(最短路,变形) WA中。。。。。
    HDU 5348 MZL's endless loop 给边定向(欧拉回路,最大流)
    HDU 5344 MZL's xor (水题)
  • 原文地址:https://www.cnblogs.com/timsheng/p/2600148.html
Copyright © 2011-2022 走看看