2015-07-03 2 views
0

Я хотел бы создать класс контейнера для объектов на основе массива Ruby. Я хотел бы манипулировать более чем одним из этих контейнеров, например, конкатенировать 2 вместе. Если я пытаюсь это:Как ссылаться на другой объект того же класса в Ruby?

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 method each' for # (NoMethodError) from ./test.rb:40:in `'

Я пробовал различные подходы, например, создавая каждый метод, как это, кажется, хочет, но не повезло до сих пор. Какие-либо предложения? И спасибо заранее!

ответ

2

Если вы хотите, чтобы иметь возможность добавить 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 
+1

Мой ленивый подход: определение функции добавления (* список) 'а затем' list.flatten.each {...} ' – tadman

+0

Если вы не хотите, чтобы иметь возможность хранить массивы массивов :) – mudasobwa

+0

Это лишило бы вещи немного, это правда. Хорошая точка зрения! – tadman

1

Я предполагаю, что должен быть геттер/сеттер методы:

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

1

Рассмотрим этот подход:

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. Добавлен новый метод + как его интуитивно понятный в этом контексте.
+0

Итак, вызов метода экземпляра 'append' похоронит экземпляр, вместо него вместо него создается новый? Я уверен, что пользователи этого кода не ожидают такого поведения. – mudasobwa

+0

Да, это было плохо. Обновился, чтобы сделать его более интуитивным. –

Смежные вопросы