3. 迭代器

3.1 什么是迭代器?

有些方法允许在调用它的过程中添加块或者过程对象, 这种特殊的方法就是迭代器。

迭代器是为了抽象化控制结构(特别是循环)而设的一种方法.

还是举个实际的例子吧. 若您想分别为集合中的每个元素来进行相同的处理的话, 就应该使用迭代器. 例如

data = [1, 2, 3]
data.each do |i|
  print i, "\n"
end

它会输出如下内容。

$ ruby test.rb
1
2
3

也就是说,分别为data中的每个元素来执行夹在doend之间的块的内容。

用C语言来改写的话,就是

int data[3] = {1, 2, 3};
int i;
for (i = 0; i < 3; i++) {
  printf("%d\n", data[i]);
}

for来编写代码时, 可能会因为误判循环边界而导致bug(虽然在本例中不会出现这种低级错误), 但使用迭代器就不必担心这些问题了:-)

另外, 除了do...end之外, 您还可以使用{...}

data = [1, 2, 3]
data.each { |i|
  print i, "\n"
}

这段代码与前面的完全等效。但这并不标明do...end{...}完全等效。例如

foobar a, b do .. end # 此时foobar被看做是迭代器
foobar a, b { .. }    # 而此时   b被看做是迭代器

这说明{ }的结合力大于do块。

3.2 怎么将块传递给迭代器?

如果想将块传递给迭代器, 只需要将块放在方法后面即可. 另外, 还可以在表示过程对象的变量/常数前添加&, 并将其作为参数传递给方法即可。

3.3 如何在主调方法中使用块?

有3种方式可以让您在方法中使用块. 它们分别是yield控制结构、块参数和Proc.new。(在由C语言写成的扩展库中,需要使用rb_yield)

使用yield时, yield后面的参数会被传递给块, 然后执行块的内容。

块参数是指,插在方法定义中的参数列表末尾的 形如&method的参数. 可以在方法中,这样method.call(args...)来进行调用。

使用Proc.new时, 它会接管传递给方法的块, 并以块的内容为范本生成一个过程对象。proclamda也是一样。

def a (&b)
  yield
  b.call
  Proc.new.call
  proc.call
  lambda.call
end
a{print "test\n"}

3.4 为什么Proc.new没有生成过程对象呢?

若没有给出块的话, Proc.new是不会生成过程对象的, 而且还会引发错误。在方法定义中插入Proc.new时, 一般都假定在方法调用时会传过来一个块。