【问题标题】:How do I reference another object of the same class in Ruby?如何在 Ruby 中引用同一类的另一个对象?
【发布时间】:2015-09-21 13:14:23
【问题描述】:

我想为基于 Ruby 数组的对象创建一个容器类。我想操作多个这些容器,比如将 2 个连接在一起。如果我试试这个:

class Thing
  attr_accessor :name
end

class Things
  def initialize
    @things = Array.new
  end

  def addone( a )
    @things.push( a )
  end

  def append( list )
    list.each { |i| addone( i ) }
  end
end

item1 = Thing.new
item2 = Thing.new
item3 = Thing.new
item4 = Thing.new

item1.name = "Marty"
item2.name = "Fred"
item3.name = "Janice"
item4.name = "John"

list1 = Things.new
list1.addone( item1 )
list1.addone( item2 )

list2 = Things.new
list2.addone( item3 )
list2.addone( item4 )

list3 = Things.new
list3 = list2.append( list1 )

我得到错误:

in append': undefined methodeach' for # (NoMethodError) from ./test.rb:40:in `'

我尝试了不同的方法,例如根据需要创建 each 方法,但到目前为止还没有运气。有什么建议?提前致谢!

【问题讨论】:

    标签: arrays ruby object containers


    【解决方案1】:

    如果您希望能够将Things 添加到Things,您有两个能力:在Things 上实现迭代器方法或简单地装饰包装的Array

    def append(list)
      case list
      when Enumerable then list.each { |i| addone(i) }
      when Things then list.instance_variable_get(:@things).each { |e| addone(i) }
      else raise "Sorry, can’t add #{list}"
    end
    

    【讨论】:

    • 我的懒惰方法:def append(*list),然后是list.flatten.each { ... }
    • 除非你希望能够存储数组的数组:)
    • @tadman BTW,我不会干扰方法签名:def append(list) 及更高版本:[*list].flatten.each
    • *list 的优点是您可以根据手头的情况将其称为append(x,y)append([x,y])
    • @tadman 好吧,我明白了,我读过一本书“Ruby for初学者”:) 根据我的经验,在声明接受:opt 参数的方法之前,必须重新考虑该方法七次。它会导致难以调试的错误、误用等。但我当然同意,无论您是否了解自己在做什么,*args 都有更大的力量。
    【解决方案2】:

    我猜应该有getter/setter方法:

    attr_accessor :things 
    

    那么你应该改变你的addone方法:

    def append(list)
       list.things.each { |i| addone( i ) } # iterate through array items, not Things instance object
       self # return appended list object instead of unchanged provided argument – list1
    end
    

    list3.things的输出:

    => [#<Context::Thing:0x00000001adea48 @name="Janice">,
        #<Context::Thing:0x00000001ade9f8 @name="John">,
        #<Context::Thing:0x00000001adea98 @name="Marty">,
        #<Context::Thing:0x00000001adea70 @name="Fred">]
    

    Demonstration

    【讨论】:

      【解决方案3】:

      考虑这种方法:

      class Thing
        attr_accessor :name
      
        def initialize(name)
          @name = name
        end
      end
      
      class Things
        def initialize(things = [])
          @things = things
        end
      
        def push(thing)
          @things.push(thing)
        end
      
        def append(other)
          @things << other.to_a
        end
      
        def +(other)
          Things.new(@things + other.to_a)
        end
      
        def to_a
          @things
        end
      end
      
      some_things = %w(Marty Fred Janice John).map { |name| Thing.new(name) }
      
      things_1 = Things.new
      some_things.first(2).each { |thing| things_1.push(thing) }
      
      things_2 = Things.new
      some_things.last(2).each { |thing| things_2.push(thing) }
      
      things_1.append(things_2) # This actually appends to things_1 rather than creating a new object
      new_things = things_1 + things_2 # Creates a new object
      
      # => #<Things:0x007ff85a1aa770 @things=[
      # #<Thing:0x007ff85a1aa928 @name="Marty">,
      # #<Thing:0x007ff85a1aa900 @name="Fred">,
      # #<Thing:0x007ff85a1aa8d8 @name="Janice">,
      # #<Thing:0x007ff85a1aa8b0 @name="John">]>
      

      注意事项:

      1. 稍微修改了 API 以简化代码。
      2. 在此上下文中添加了一个新方法 +,因为它很直观。

      【讨论】:

      • 那么,调用实例方法append 埋葬一个实例,而不是提升一个新实例?我敢打赌这段代码的用户不会期待这种行为。
      • 是的,这很糟糕。已更新以使其更直观。
      猜你喜欢
      • 2012-11-25
      • 1970-01-01
      • 1970-01-01
      • 2018-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-16
      相关资源
      最近更新 更多