1

我有一个类game,其中包含一些自定义对象数组(恐龙、cacemen 等),这些对象由不同的访问器返回,例如game.dinosaursgame.cavemen

目前,所有这些访问器都只返回内部存储的数组。但是现在我想为这些访问器返回的这些数组添加一些自定义迭代方法,以便能够编写game.dinosaurs.each_carnivore { ... }类似于. 但是从我的访问器返回的对象仍然必须表现得像数组。each_elementeach_attrLibXML::XML::Nodegame.dinosaursgame.cavemen

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

我知道我可以在我的集合上使用mapselect在外部使用,但我想在内部封装这些迭代,以便我的班级的用户不需要费心如何设置迭代来从内部数组中只选择食肉恐龙。

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

4

3 回答 3

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

这是一个简单的组合和委托:

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

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

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

但也有自定义迭代器:

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}" }

输出:

Dinosaurs:
1. Tyrannosaurus
2. Allosaurus
3. Apatosaurus

Carnivores:
1. Tyrannosaurus
2. Allosaurus

Herbivores:
1. Apatosaurus
于 2013-08-30T06:47:40.663 回答
1

您可以通过使用红宝石块来做到这一点。阅读更多

这里的简单示例:

class Game

  def initialize
    @carnivoures = [1,2,3]
  end

  def each_carnivoures
    @carnivoures.each do |carni|
      yield carni
    end
  end

end

Game.new.each_carnivoures{ |c| p c}
于 2013-08-30T05:48:20.190 回答
0

有可能链接这样的过滤器也很好。您可以简单地通过将方法包装select到自定义方法中来实现这一点,返回您的新类而不是数组。您也可以包装一些其他方法,例如map

class Units < Array
  def select
    self.class.new(super)
  end

  def dinosaurs
    select{ |unit| unit.kind == 'dinosaur' }
  end

  def cavemen
    select{ |unit| unit.kind == 'caveman' }
  end

  def carnivore
    select{ |unit| unit.type == 'carnivore' }
  end

  def herbivore
    select{ |unit| unit.type == 'herbivore' }
  end
end

Units.dinosaurs.carnivore
Units.cavemen.herbivore
于 2018-04-23T18:43:42.163 回答