【问题标题】:Ruby class variable instance variable bugRuby 类变量实例变量错误
【发布时间】:2021-07-29 23:51:41
【问题描述】:

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 行,每行都有一个带有 2 个数字的字符串的框架 ID 但是,返回以下内容

"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">]

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

【问题讨论】:

  • 改用@frames = Array.new(10) { Frame.new }
  • 提示:当得到这样奇怪的结果时,用p @frames.map(&amp;:object_id)做一点调试,看看你是否有一个相同的对象引用数组,或者不同的。

标签: ruby oop instantiation instance-variables class-variables


【解决方案1】:

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

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

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

这个sn-p代码可以帮你解决

@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 }

【讨论】:

  • 是的,但不是。您可以只使用块构造函数。不需要其他混乱。
  • @tadman jejejej 是的!你是对的,我会将该选项添加到 sn-p 代码中,供参考
【解决方案2】:
Array.new(10, Frame.new)

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

Array.new(10) { Frame.new }

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

见:Creating Arrays

也可以通过显式调用::new 来创建一个数组,使用零, 一个(Array 的初始大小)或两个参数(初始大小 和一个默认对象)。

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

请注意,第二个参数使用对 相同的对象。因此,仅在您 需要使用本机不可变对象实例化数组,例如 符号、数字,真假。

要创建一个包含单独对象的数组,可以传递一个块 反而。此方法可以安全地用于可变对象,例如 哈希、字符串或其他数组:

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

【讨论】:

  • @engineersmnky 我同意,文档应该包含一个示例,说明这在实践中意味着什么。这是一个很常见的陷阱。
  • 2.7 文档中曾经有一个common gotchas 部分。不幸的是,它在 Ruby 3 文档中被删除了。
猜你喜欢
  • 2014-09-21
  • 2013-03-24
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 2010-10-28
  • 2015-04-29
  • 2014-10-23
  • 1970-01-01
相关资源
最近更新 更多