我正在尝试使用Enumerable和Comparable。我阅读了文档并希望尝试一下。
class Apple
attr_accessor :color
end
class Fruit
include Enumerable
include Comparable
attr_accessor :apples
def initialize
@apples = []
end
def <=> o
@apple.color <=> o.color
end
def each
@apples.each {|apple| yield apple }
end
def to_s
"apple: #{@apple.color}"
end
end
fruit = Fruit.new
a1 = Apple.new
a1.color = :red
a2 = Apple.new
a2.color = :green
a3 = Apple.new
a3.color = :yellow
fruit.apples.push a1
fruit.apples.push a2
fruit.apples.push a3
有两件事没有按预期工作。所以我重写to_s,我希望数组的每个索引都包含一个类似&#34; apple:red&#34;的字符串。相反,我得到了这个:
fruit.sort
=> [#<Apple:0x007fbf53971048 @apples=[], @color=:green>, #<Apple:0x007fbf53999890 @apples=[], @color=:red>, #<Apple:0x007fbf5409b530 @apples=[], @color=:yellow>]
第二个问题是当我包含Enumerable时,Enumerable的实例方法应该在继承的类之前添加到祖先链中。这应该包括Enumerable的方法,如with_each,reduce等等到祖先链。但是,当我这样做时:
fruit.each.with_index(1).reduce({}) do |acc,(apple,i)|
acc << { i => apple.color}
end
LocalJumpError: no block given (yield)
如您所见,我得到一个LocalJumpError。我期待这样的结果:
{ 1 => :red, 2 => :green, 3 => :yellow}
我做错了什么?我定义了each
,就像我应该的那样,它没有像预期的那样工作。
答案 0 :(得分:3)
当没有给出阻止时返回Enumerator::Lazy#enum_for
:
def each
@apples.each
end
Array
就是这样做的,因此上面已经可以了。里面的代码实际上类似于:
def each
return enum_for(:each) unless block_given?
@apples.each { |apple| yield apple }
end
您在pry
/ irb
中看到的内容是inspect
的结果,而不是to_s
。
答案 1 :(得分:2)
我重写to_s,我希望数组的每个索引都包含一个像“apple:red”这样的字符串。相反,我得到了这个:...
这里有两件事是错误的。
1)您必须实施Apple#to_s
,而不是Fruit#to_s
:
class Apple
attr_accessor :color
def to_s
"apple: #{color}"
end
end
class Apple
attr_accessor :color
def to_s
"apple: #{color}"
end
alias inspect to_s
end
这会给你:
fruit = Fruit.new
a1 = Apple.new
a1.color = :red
a2 = Apple.new
a2.color = :green
a3 = Apple.new
a3.color = :yellow
fruit.apples.push a1
fruit.apples.push a2
fruit.apples.push a3
fruit
#=> #<Fruit:0x00007faa3686b7c0 @apples=[apple: red, apple: green, apple: yellow]>
第二个问题是当我包含Enumerable时,Enumerable的实例方法应该被添加到祖先链......
当你写:
fruit.each.with_index(1)
您在with_index
的返回值上调用each
。这就是错误发生的地方:
fruit.each
#=> LocalJumpError: no block given (yield)
当没有给出阻止时,您必须返回Enumerator
的实例。这可以使用条件(参见mudasobwa's answer)或通过块传递来实现:
def each(&block)
@apples.each(&block)
end
您的代码还有另一个问题:不是Fruit
,而Apple
是应该实施<=>
并包含Comparable
的类。因为在排序@apples
时,项目会相互比较:
class Apple
include Comparable
attr_accessor :color
def <=> o
color <=> o.color
end
# ...
end
请注意,包含Enumerable
时有一个问题。虽然您可以使用所有这些方法,但您很容易丢失包装类并最终得到一个普通数组:
fruit
#=> #<Fruit:0x00007faa3686b7c0 @apples=[apple: red, apple: green, apple: yellow]>
fruit.sort
#=> [apple: green, apple: red, apple: yellow]