Ruby class 变量实例变量bug

Ruby class variable instance variable bug

Game 有一个包含十个 Frame 个实例的数组

class Frame
  attr_accessor :rolls
  def initialize
    @rolls = ""
  end
  
end

class Game
  attr_accessor :frames
  def initialize
    @frames = Array.new(10, Frame.new) 
  end
  
  def print_frames
    @frames.each_with_index do |frame, idx|
      p "Frame ##{idx+1}: #{frame.rolls}"
    end
  end
end

game = Game.new

rolls = [5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2]

curr = 0
rolls.each_with_index do |roll|
  game.frames[curr].rolls << roll.to_s
  if game.frames[curr].rolls.size == 2
    curr += 1
  end
end

p "Total rolls: #{rolls.size}"
p game.print_frames

我希望打印 10 行,每行都有一个 frame id 和一个由 2 个数字组成的字符串 但是,返回以下内容

"Total rolls: 20"
"Frame #1: 55374000000000000022"
"Frame #2: 55374000000000000022"
"Frame #3: 55374000000000000022"
"Frame #4: 55374000000000000022"
"Frame #5: 55374000000000000022"
"Frame #6: 55374000000000000022"
"Frame #7: 55374000000000000022"
"Frame #8: 55374000000000000022"
"Frame #9: 55374000000000000022"
"Frame #10: 55374000000000000022"
[#<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">, #<Frame:0x0000565402e40528 @rolls="55374000000000000022">]

实例变量的作用类似于 class 帧之间共享的变量。所有 20 个卷数都连接在一起,而不是每个帧拥有一对。
代码有什么问题?是因为 GameFrame 对象没有正确实例化吗?

方法 Array.new 定义为 new(size=0, default=nil) 这意味着第二个参数将是数组中所有对象的值,默认值。

这就是为什么对于您的情况,所有 10 个对象都是相同的。

https://ruby-doc.org/core-2.7.0/Array.html#method-c-new

这段代码可以帮助您解决

@frames = []
10.times {|i| @frames.push(rand) }
=> [0.7053319996471655, 0.34131818323294594, 0.4084836724883256, 0.20452172335941388, 0.5124065818560665, 0.4203474973940552, 0.6719502264788891, 0.7453268015406016, 0.09500886225101768, 0.9053707563920769]

# try this for your case
# 10.times {|i| @frames.push(Frame.new) } 

# using array creation with block
# ty tadman for the advice
# @frames = Array.new(10) { Frame.new }

Array.new(10, Frame.new)

创建一个包含 10 个元素的数组,所有元素都指向一个 Frame 实例。要创建包含 10 个独立 Frame 实例的数组,您应该使用块形式。

Array.new(10) { Frame.new }

这将执行块 10 次并将每次执行的结果分配给共同发起索引。

参见:Creating Arrays

An array can also be created by explicitly calling ::new with zero, one (the initial size of the Array) or two arguments (the initial size and a default object).

ary = Array.new    #=> []
Array.new(3)       #=> [nil, nil, nil]
Array.new(3, true) #=> [true, true, true]

Note that the second argument populates the array with references to the same object. Therefore, it is only recommended in cases when you need to instantiate arrays with natively immutable objects such as Symbols, numbers, true or false.

To create an array with separate objects a block can be passed instead. This method is safe to use with mutable objects such as hashes, strings or other arrays:

Array.new(4) {Hash.new}    #=> [{}, {}, {}, {}]
Array.new(4) {|i| i.to_s } #=> ["0", "1", "2", "3"]