要让一个类可以枚举(Enumerable),那么就是需要他具有一个each方法。同时include Enumerable。(mixin模式)
String不是Enumberable的,因为他有三个迭代器(Iterator),但是不包含each。
each返回一个Enumerator,可以用它来外部访问进行枚举。例如
enu = my_array.each
enu.next #类似游标,获取元素
注意到尾部后继续next会返回一个StopIteration异常。
也可以通过获取该enumerator来直接调用map等函数。例如
"xyz".each_char.map{|x| puts x} #等价于 "xyz".enum_for(:each_char).map
顺便说一句,enum_for和to_enum的实现完全相同。类似的还可以使用Enumberator.new。
Enumberator.new有两种形式:
new(size = nil) { |yielder| ... }
new(obj, method = :each, *args)
第一种的直接用block生成一个枚举器。例如
fib = Enumberator.new do |y|
y<<1
y<<2
y<<3
end
第二种其实就是将enum_for,to_enum的caller作为obj,参数即method,最后是参数。(这是ruby常用的调用方法的方法)
"string".to_enum(:each_char)
等价于
Enumerator.new("string",:each_char)
自己构造类似Array的可枚举类型的时候,只需要实现each,然后
include Enumerable
(module) 就可以实现map,select等枚举器。其实这些枚举器都是建立在each基础上的。
each的实现主要是使用yield,在each定义中循环执行 yield x #x当前变量。
在each的实现中,应该区分判断如果有block,block_given?就调用block,否则就返回一个Enumberator对象。