2016-08-02 2 views
0

Допустим, у меня есть класс счетчика в рубин, определяемый какЗаполнение массива рубиновый с объекта по умолчанию

class Counter 
    attr_accessor :starting_value 
    def initialize(starting_value) 
     @starting_value = starting_value 
    end 

    def tick 
     @starting_value = @starting_value + 1 
    end 
end 

, и я хочу, чтобы заполнить массив с этим объектом, используя параметр по умолчанию, например: counter_arr = Array.new(5, Counter.new(0))

Это почти то, что я хочу, за исключением того, что у меня теперь есть массив, содержащий тот же экземпляр счетчика 5 раз, вместо массива из 5 новых счетчиков. IE, когда я запускаю код

counter_arr = Array.new(5, Counter.new(0)) 
counter_arr[0].tick 
counter_arr.each do |c| 
    puts(c.starting_value) 
end 

выход I

1 
1 
1 
1 
1 

вместо

1 
0 
0 
0 
0 

Мне было интересно, что такое "рубин-эск" способ инициализировать массив с кратному новые экземпляры объекта?

+1

Не забывайте синтаксические функции, такие как '@starting_value + = 1'. Это обычно меньше подробностей и позволяет избежать опечаток. – tadman

+1

Этот код в значительной степени соответствует линии, аналогичной примеру в разделе «Common gotchas» документов для «Array :: new» (http://ruby-doc.org/core/Array.html # method-c-new-label-Common + gotchas), которые отвечают на этот вопрос. –

ответ

4

Одним из первых крупных камней преткновения люди сталкиваются при изучении Ruby, если они не знакомы с языком, который использует объект реверансы pervasively как они работают.

Массив представляет собой набор ссылок на ноль или более других объектов. Эти объекты не обязательно уникальны, и в некоторых случаях они все одинаковы. Вы создаете такой объект здесь:

counters = Array.new(5, Counter.new(0)) 

Это создает уникальный объект счетчика и заполнит все 5 слотов массива с ним. Это происходит потому, что аргументы методов обрабатываются один раз до вызова метода. Вы можете это проверить:

counters.map(&:object_id) 

Это возвращает уникальный идентификатор объекта для каждого объекта в массиве. Они будут случайными значениями, каждый процесс отличается, но они будут одинаковыми.

Путь, чтобы исправить это использовать блок инициализатора:

counters = Array.new(5) do 
    Counter.new(0) 
end 

Это не вставить один и тот же объект, но результат вычисления этого блока каждый раз, и так, что инициализирует новый объект счетчика , объекты будут уникальными.

Один из способов привести в порядок это вверх, чтобы настроить Counter объект иметь вменяемый по умолчанию:

class Counter 
    def initialize(initial = nil) 
    @value = initial.to_i 
    end 

    def tick 
    @value += 0 
    end 
end 

Это имеет преимущество принимать произвольные значения, даже те, которые не обязательно правильный тип. Теперь Counter.new('2') будет работать с преобразованием этого значения автоматически. Это основной принцип Duck Typing. Если он может дать вам номер, это будет так же хорошо, как число.

+0

спасибо! Этот код был буквально первыми 21 строкой рубина, который я когда-либо писал, поэтому я ценю все улучшения. – dustinroepsch

+0

Надеюсь, что это сработает для вас. Похоже, вы уже ушли и бежите, что приятно видеть. – tadman

+0

Очень четкий ответ. Возможно, 'def initialize (initial = 0); @value = initial; end'. –

2

Попробуйте

counter_arr = Array.new(5) { Counter.new(0) } 
1
counter_arr = ([-> { Counter.new(0) }] * 5).map &:call 
+1

Это довольно сумасшедшее решение, но это по крайней мере роман. – tadman

+0

Я думаю, что другие ответы дают более чистые решения, но мне все еще интересно, что здесь происходит – dustinroepsch

+0

Ответы Урсуса выражены наизнанку. – sawa

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