假设我在ruby中有一个Counter类,定义为
class Counter
attr_accessor :starting_value
def initialize(starting_value)
@starting_value = starting_value
end
def tick
@starting_value = @starting_value + 1
end
end
Run Code Online (Sandbox Code Playgroud)
我想使用默认参数用该对象填充数组,如下所示:
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
Run Code Online (Sandbox Code Playgroud)
我输出
1
1
1
1
1
Run Code Online (Sandbox Code Playgroud)
代替
1
0
0
0
0
Run Code Online (Sandbox Code Playgroud)
我想知道,用对象的多个新实例初始化数组的“红宝石式”方法是什么?
如果他们不熟悉一种普遍使用对象崇敬的语言,那么当他们学习Ruby时,人们遇到的第一个主要障碍就是它们的工作原理。
数组是对零个或多个其他对象的引用的集合。这些对象不一定是唯一的,并且在某些情况下它们都是相同的。您在这里创建这样的对象:
counters = Array.new(5, Counter.new(0))
Run Code Online (Sandbox Code Playgroud)
这将创建一个单一的Counter对象,并使用它填充数组的所有5个插槽。之所以会这样,是因为方法的参数在调用方法之前先被评估。您可以对此进行测试:
counters.map(&:object_id)
Run Code Online (Sandbox Code Playgroud)
这将返回数组中每个对象的唯一对象ID。它们将是随机值,每个过程都不同,但是它们将是相同的。
解决此问题的方法是使用块初始化程序:
counters = Array.new(5) do
Counter.new(0)
end
Run Code Online (Sandbox Code Playgroud)
那不会插入相同的对象,而是每次评估该块的结果,并且由于它初始化了一个新的Counter对象,因此这些对象将是唯一的。
整理此问题的一种方法是将Counter对象调整为默认值:
class Counter
def initialize(initial = nil)
@value = initial.to_i
end
def tick
@value += 0
end
end
Run Code Online (Sandbox Code Playgroud)
这样做的好处是可以接受任意值,即使那些值不一定是正确的类型。现在Counter.new('2'),该值将被自动转换。这是鸭子打字的基本原理。如果能给你一个数字,那就和数字一样好。