2016-08-02 32 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个新计数器的数组。即,当我运行该代码的

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

这段代码与'Array :: new'文档的[“Common gotchas”部分](http://ruby-doc.org/core/Array)中的例子几乎一致。html#method-c-new-label-Common + gotchas)它回答了这个问题。 –

回答

4

其中第一个主要障碍学习Ruby的时候,如果他们不熟悉与使用对象reverences普遍地语言的人遇到的是如何工作的。

数组是一个对零个或多个其他对象的引用的集合。这些对象不一定是唯一的,在某些情况下它们都是相同的。您在这里创建这样一个对象:

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

这将创建一个单一的计数器对象并填充所有5个插槽,它的阵列。这是因为方法的参数在调用方法之前被计算一次。你可以测试这个:

counters.map(&:object_id) 

返回数组中每个对象的唯一对象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')将与该值自动转换。这是鸭子打字的基本原则。如果它可以给你一个数字,它就像一个数字一样好。

+0

谢谢!该代码实际上是我写过的前21行红宝石,所以我很欣赏所有的改进。 – dustinroepsch

+0

希望能为你解决。看起来你已经在跑步了,这很好看。 – tadman

+0

非常明确的答案。也许'def初始化(初始= 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