我有一个班级game
,其中包含一些自定义对象(恐龙,卡塞尔等)的数组,这些对象由不同的访问者返回,例如game.dinosaurs
,game.cavemen
等。< / p>
目前,所有这些访问器只返回内部存储的数组。但是现在我想为这些访问器返回的这些数组添加一些自定义迭代方法,以便能够编写game.dinosaurs.each_carnivore { ... }
等代码,类似于each_element
和each_attr
LibXML::XML::Node
中的迭代器。但是从我的访问器game.dinosaurs
和game.cavemen
返回的对象必须表现得像数组一样。
通常在Ruby中如何做到这一点?
我应该从我的访问器返回的对象是从Ruby的Array
类派生的一些自定义类吗?或者我应该创建一个混合了Enumerable
的自定义类?
我知道我可以在我的馆藏外部使用map
或select
,但我想在内部封装这些迭代,我们班级的用户不需要打扰如何设置迭代以从内部阵列中仅选择食肉动物恐龙。
编辑:我没有询问如何使用迭代器或如何实现它们,但是如何只添加一些自定义迭代器到以前只是普通数组的对象(并且仍然需要是)。
答案 0 :(得分: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
答案 1 :(得分:1)
您可以通过使用ruby块来完成此操作。 Read more
这里有一个简单的例子:
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}
答案 2 :(得分: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