【学习笔记】RUBY 块和迭代器
迭代器:
1、一个Ruby迭代器就是一个简单的能接收代码块的方法(比如each这个方法就是一个迭代器)。特征:如果一个方法里包含了yield调用,那这个方法肯定是迭代器
2、迭代器方法和块之间有如下传递关系:块被当成一个特殊参数传给迭代器方法,而迭代器方法内部在使用yield调用代码块时可将参数值传入块。
3、实际上,迭代器的功能就是一种回调!迭代器方法所属的类只负责遍历需要遍历的元素,而对元素所做的处理则通过回调代码块来实现。
4、Ruby中的容器对象(如数组、Range和Hash对象等)都包含了两个简单的迭代器,分别是each和collect。each可以认为是最简单的迭代器,它会对集合的每个元素调用块。 collect,将容器中的元素传递给一个块,在块中处理后返回一个包含处理结果的新数组。
两个经典例子:
# 定义一个Apple类 class Apple # 定义Apple类的构造器 def initialize(name, color, weight) @name = name; @color = color; @weight = weight; end # 使用存储器定义了三个属性 attr :name attr :color attr :weight # 定义一个show方法,该show方法可以作为Apple类的迭代器 def show yield :name , @name yield :color , @color yield :weight , @weight end end # 创建一个Apple实例 apple = Apple.new("红苹果" , "红色" , "0.4") # 使用apple的show迭代器 apple.show do |name , value| puts "这个苹果的" + name.to_s + "变量值是:" + value.to_s end
class Array # 为Array增加一个find方法 def find # 使用for循环迭代数组的所有数组元素 for i in 0...size value = self # yield调用时将数组元素值传入代码块 # 调用结束后将代码块的返回值(boolean值)传回该方法体 return value if yield value end return nil end end # 使用数组的find方法 puts [1, 3, 5, 7, 9].find {|v| v * v > 30 }
块以及闭包:
1、do .... end 这就是一个块,CODE BLOCK,或者叫匿名方法
2、块(block):只是一段代码,相当于一个匿名函数;闭包(Closures):一段代码,能作为参数传递给其它方法。
3、Ruby的4种闭包:blocks, Procs, lambdas 和 Methods。是Ruby中最强大的一部分,同时也是最容易迷惑的。(理解Ruby的4种闭包:blocks, Procs, lambdas 和 Methods。 http://rubyer.me/blog/917 )
4、
----------------------------------------------------------
实现迭代器
----------------------------------------------------------
英文来自http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_containers.html
中文来自http://blog.csdn.net/ruby_cn/article/details/192094
Implementing Iterators
实现迭代器
A Ruby iterator is simply a method that can invoke a block of code. At first sight, a block in Ruby looks just like a block in C, Java, or Perl. Unfortunately, in this case looks are deceiving---a Ruby block is a way of grouping statements, but not in the conventional way.
一个Ruby迭代器就是一个简单的能接收代码块的方法(比如each这个方法就是一个迭代器)。第一眼看上去,Ruby中的block像C,Java,Perl中的一样,但是实际上是有不同的。
First, a block may appear only in the source adjacent to a method call; the block is written starting on the same line as the method's last parameter. Second, the code in the block is not executed at the time it is encountered. Instead, Ruby remembers the context in which the block appears (the local variables, the current object, and so on), and then enters the method. This is where the magic starts.
首先,块在源代码中紧挨着方法调用,并且和这个方法的最后一个参数写在同一行上。其次,这个块不会立即被执行,Ruby首先会记住这个块出现的上下文(局部变量,当前对象等),然后进入方法,这里也是魔术开始的地方。
Within the method, the block may be invoked, almost as if it were a method itself, using the yield statement. Whenever a yield is executed, it invokes the code in the block. When the block exits, control picks back up immediately after the yield.[Programming-language buffs will be pleased to know that the keyword yield was chosen to echo the yield function in Liskov's language CLU, a language that is over 20 years old and yet contains features that still haven't been widely exploited by the CLU-less.] Let's start with a trivial example.
在方法里面,这个块才会用yield来调用执行,就像这个块是方法本身一样,每当yield在方法中被执行,这个块就会被调用。当这个块执行完退出后,控制将交给yield后面的语句(yield来自一个有20多年历史的语言:CLU)。我们来看一个小例子。
def threeTimes yield yield yield end threeTimes { puts "Hello" }
produces: Hello Hello Hello
The block (the code between the braces) is associated with the call to the method threeTimes. Within this method, yield is called three times in a row. Each time, it invokes the code in the block, and a cheery greeting is printed. What makes blocks interesting, however, is that you can pass parameters to them and receive values back from them. For example, we could write a simple function that returns members of the Fibonacci series up to a certain value.[The basic Fibonacci series is a sequence of integers, starting with two 1's, in which each subsequent term is the sum of the two preceding terms. The series is sometimes used in sorting algorithms and in analyzing natural phenomena.]
这个块(用两个大括号定义)赋给了一个方法threeTimes,在这个方法里面,yield执行了3次,每次执行它都会调用给定的block,即打印一个欢迎语句。使块变得有趣的是你可以给块传递参数,并且从块中得到结果。下面例子,我们将会得到小于一个指定值得Fibonacci 数列。
def fibUpTo(max) i1, i2 = 1, 1 # parallel assignment while i1 <= max yield i1 i1, i2 = i2, i1+i2 end end fibUpTo(1000) { |f| print f, " " }
produces: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
In this example, the yield statement has a parameter. This value is passed to the associated block. In the definition of the block, the argument list appears between vertical bars. In this instance, the variable f receives the value passed to the yield, so the block prints successive members of the series. (This example also shows parallel assignment in action. We'll come back to this on page 75.) Although it is common to pass just one value to a block, this is not a requirement; a block may have any number of arguments. What happens if a block has a different number of parameters than are given to the yield? By a staggering coincidence, the rules we discuss under parallel assignment come into play (with a slight twist: multiple parameters passed to a yield are converted to an array if the block has just one argument).
在这个例子中,yield接收一个参数,这个参数将会在执行的时候传递给指定的块。在块的定义中,参数用两个竖线括起来,放在最前面。在这个例子中f用来接收yield传递的参数,所以,这个块才能打印这个序列。一个块可以接受任意个参数。如果一个块的参数和yield中传递的参数个数不一样,将会怎样呢?很巧合,这和我们在并行赋值(parallel assignment)中谈到的原则一样(如果一个block只接收一个参数,而yield提供的参数多于1个,那么这些参数将被转化为一个数组。)
传递给一个块的参数可以是存在地局部变量,如果是这样的话,那么这个局部变量的新值(如果在块中被修改了)在块退出后将会保留,这可能会有一定的副作用,但是这样做有一个性能方面的考率。
Parameters to a block may be existing local variables; if so, the new value of the variable will be retained after the block completes. This may lead to unexpected behavior, but there is also a performance gain to be had by using variables that already exist.[For more information on this and other ``gotchas,'' see the list beginning on page 127; more performance information begins on page 128.]
A block may also return a value to the method. The value of the last expression evaluated in the block is passed back to the method as the value of the yield. This is how the find method used by class Array works.[The find method is actually defined in module Enumerable, which is mixed into class Array.] Its implementation would look something like the following.
一个块也可以返回一个结果给调用它的方法。这个块中的最后一个表达式的值将会返回给方法,Array中的find方法就是这样工作的。(find在Enumerable
中定义,被插入到了类Array
)
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
This passes successive elements of the array to the associated block. If the block returns true, the method returns the corresponding element. If no element matches, the method returns nil. The example shows the benefit of this approach to iterators. The Array class does what it does best, accessing array elements, leaving the application code to concentrate on its particular requirement (in this case, finding an entry that meets some mathematical criteria).
这个用法中数组将连续的元素传递给指定的块,如果这个块返回true,则这个方法返回当前对应的元素值,如果没有符合的值,则返回nil。这个方法显示了迭代器的好处,Array类只作自己应该做的,访问数组元素,而应用代码只关注于特殊的需求。
Some iterators are common to many types of Ruby collections. We've looked at find already. Two others are each and collect. each is probably the simplest iterator---all it does is yield successive elements of its collection.
Ruby中的集合对象中也包含其它一些常用迭代器,其中之二是each和collect。each可以认为是最简单的迭代器,它们都会对集合的每个元素来调用块。
[ 1, 3, 5 ].each { |i| puts i } produces: 1 3 5
The each iterator has a special place in Ruby; on page 85 we'll describe how it's used as the basis of the language's for loop, and starting on page 102 we'll see how defining an each method can add a whole lot more functionality to your class for free.
Another common iterator is collect, which takes each element from the collection and passes it to the block. The results returned by the block are used to construct a new array. For instance:
另一个是collect,它跟each类似,它将集合中的元素传递给一个块,在块中处理后返回一个包含处理结果的新数组。
["H", "A", "L"].collect { |x| x.succ } » ["I", "B", "M"]
其他:
关于 迭代器 Iterators 的解释:RUBY 使用手册:http://guides.ruby.tw/ruby/iterators.html
Ruby | Block和迭代器 http://blackanger.blog.51cto.com/140924/23876/
深入理解Blocks,Procs和lambdas http://blackanger.blog.51cto.com/140924/123034
在Ruby中实现迭代器 http://free-dem.iteye.com/blog/218123