文章目錄
迭代器:
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 yieldendthreeTimes { puts "Hello" }
produces:HelloHelloHello
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 endendfibUpTo(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 endend[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:135
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