【问题标题】:Using methods of Enumerable in custom Class在自定义类中使用 Enumerable 的方法
【发布时间】:2018-06-15 05:56:22
【问题描述】:

我正在试验 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,我希望数组的每个索引都包含一个像“apple:red”这样的字符串。相反,我得到了这个:

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,但它没有按预期工作。

【问题讨论】:

  • 您的命名约定有点奇怪。大多数人会认为苹果是一种水果。有一个可以包含苹果的水果是很令人困惑的。

标签: ruby


【解决方案1】:

在没有给出块时返回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

【讨论】:

    【解决方案2】:

    我重写了 to_s,我希望数组的每个索引都包含一个字符串,如“apple: red”。相反,我得到了这个:...

    这里有两个问题。

    1) 你必须实现Apple#to_s,而不是Fruit#to_s

    class Apple
      attr_accessor :color
    
      def to_s
        "apple: #{color}"
      end
    end
    

    2) 你必须实现inspect 或将其定义为alias

    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 是应该实现&lt;=&gt; 并包含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]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多