Ruby自定义迭代器

Sas*_*asQ 1 ruby arrays collections each extend

我有一个类game,其包含自定义对象(恐龙,cacemen等),由不同的访问器返回,如某些阵列game.dinosaurs,game.cavemen等等.

目前,所有这些访问器只返回内部存储的数组.但是现在我想为这些访问器返回的这些数组添加一些自定义迭代方法,以便能够编写game.dinosaurs.each_carnivore { ... }类似于each_element和each_attr迭代器等代码LibXML::XML::Node.但对象从我的访问器返回game.dinosaurs,并game.cavemen要表现得象阵依然.

这样的事情通常是如何在Ruby中完成的?我应该从我的访问器返回的对象是从Ruby的Array类派生的一些自定义类吗?或者我应该创建一个Enumerable混合的自定义类?

我知道我可以在我的集合中使用map或select外部,但我想在内部封装这些迭代,我的类的用户不需要费心如何设置迭代来从内部数组中仅选择食肉动物恐龙.

编辑:我不是在询问如何使用迭代器或如何实现它们,而是如何将一些自定义迭代器添加到以前只是普通数组的对象中(并且仍然需要).

Ste*_*fan 7

这取决于(一如既往).您可以使用数组子类,您可以构建自定义类并使用组合和委派.这是一个带有数组子类的简单示例:

class DinosaurArray < Array
  def carnivores
    select { |dinosaur| dinosaur.type == :carnivore }
  end

  def herbivores
    select { |dinosaur| dinosaur.type == :herbivore }
  end

  def each_carnivore(&block)
    carnivores.each(&block)
  end

  def each_herbivore(&block)
    herbivores.each(&block)
  end
end
Run Code Online (Sandbox Code Playgroud)

这是一个简单的组合和授权:

class DinosaurArray
  def initialize
    @array = []
  end

  def <<(dinosaur)
    @array << dinosaur
  end

  def carnivores
    @array.select { |dinosaur| dinosaur.type == :carnivore }
  end

  def herbivores
    @array.select { |dinosaur| dinosaur.type == :herbivore }
  end

  def each(&block)
    @array.each(&block)
  end

  def each_carnivore(&block)
    carnivores.each(&block)
  end

  def each_herbivore(&block)
    herbivores.each(&block)
  end
end
Run Code Online (Sandbox Code Playgroud)

这两种实现都可以这样使用:

require 'ostruct'

dinosaurs = DinosaurArray.new
dinosaurs << OpenStruct.new(type: :carnivore, name: "Tyrannosaurus")
dinosaurs << OpenStruct.new(type: :carnivore, name: "Allosaurus")
dinosaurs << OpenStruct.new(type: :herbivore, name: "Apatosaurus")

puts "Dinosaurs:"
dinosaurs.each.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
puts
Run Code Online (Sandbox Code Playgroud)

但也有自定义迭代器:

puts "Carnivores:"
dinosaurs.each_carnivore.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
puts

puts "Herbivores:"
dinosaurs.each_herbivore.with_index(1) { |dinosaur, i| puts "#{i}. #{dinosaur.name}" }
Run Code Online (Sandbox Code Playgroud)

输出:

Dinosaurs:
1. Tyrannosaurus
2. Allosaurus
3. Apatosaurus

Carnivores:
1. Tyrannosaurus
2. Allosaurus

Herbivores:
1. Apatosaurus
Run Code Online (Sandbox Code Playgroud)