【问题标题】:Ruby Hash initialize confusionRuby Hash 初始化混乱
【发布时间】:2015-11-30 08:27:03
【问题描述】:

我有两节课。

class Sky
  attr_accessor :args
  def initialize(args)
    @args = args
    puts 'Initializing sky'
  end
end


class ShadowMask
  attr_accessor :sky

  def initialize(args)
    args.each{|k, v| p "#{k}: #{v.to_s}"}
    @sky = args.fetch(:sky, Sky.new({}))
  end
end

ShadowMask 可以使用默认的Sky 创建:

sm_default = ShadowMask.new({})
# Initializing sky
# => #<ShadowMask:0x007fa215230eb0 @sky=#<Sky:0x007fa215230e60 @args={}>>
sm_default.sky
# => #<Sky:0x007fa215230e60 @args={}>
sm_default.sky.args
# => {}

或者使用之前创建的Sky

skyobj = Sky.new("Sky Object")
# Initializing sky
# => #<Sky:0x007fa21481a020 @args="Sky Object">
sm = ShadowMask.new(:sky => skyobj)
# "sky: #<Sky:0x007fa21481a020>"
# Initializing sky
# => #<ShadowMask:0x007fa21521ae80 @sky=#<Sky:0x007fa21481a020 @args="Sky Object">>

在第二种情况下,Sky 的实例已经存在,我不想看到 Sky 初始化的输出 Initializing sky

我的实际代码的问题在于

puts 'Initializing sky'

是对执行若干计算以完成初始化并设置若干属性的方法的调用。每次创建 ShadowMask 时都会重复此操作。

有趣的是,如果我替换

@sky = args.fetch(:sky, Sky.new({}))

类似

@sky = args.fetch(:sky, 'AnyString')

它工作正常,但如果需要,我会放弃创建新 Sky 的可能性。

我不确定问题出在语法上,还是我犯了概念上的错误。

【问题讨论】:

    标签: ruby oop hash initialization-list


    【解决方案1】:

    我认为您需要将块传递给fetch 才能看不到Initializing sky

    @sky = args.fetch(:sky) {Sky.new({})}
    

    这背后的想法是,当您调用任何方法时,最初将调用其参数(在本例中为 Sky.new({}))。当您传递块时 - 它将在内部方法 fetch 之后调用,而不是之前。

    【讨论】:

    • 是的,方法参数在调用方法之前进行评估。这意味着在这里实例化一个Sky
    • 所以如果我理解正确::skySky.new({}) 在调用 fetch 之前进行评估?
    【解决方案2】:

    如果您只想在省略键 :sky 时提供默认的 Sky 对象,那么 fetch 是次优选择。这样会更好:

    @sky = args[:sky] || Sky.new({})
    

    fetch 的“问题”是这将导致 sm.sky 为零:

    sm = ShadowMask.new(sky: nil)
    

    如果这是您想要的行为,请使用fetch。如果没有,请使用||

    【讨论】:

    • 所以 || args[:sky] 首先被评估,并且只有当它是 falsenil 然后 Sky.new({}) 才会被评估。对吗?
    • @Rojj:是的,这叫做“短路评估”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-28
    • 1970-01-01
    相关资源
    最近更新 更多