【问题标题】:NoMethodError when trying to access Ruby hash, yet works in irb尝试访问 Ruby 哈希时出现 NoMethodError,但在 irb 中有效
【发布时间】:2019-07-07 22:04:38
【问题描述】:

我正在用 Ruby 构建一个具有以下哈希的收银机:



    @change = [
                  { :denomination => 0.01, :amount => 5 },
                  { :denomination => 0.02, :amount => 5 },
                  { :denomination => 0.05, :amount => 5 },
                  { :denomination => 0.10, :amount => 5 },
                  { :denomination => 0.20, :amount => 5 },
                  { :denomination => 0.50, :amount => 5 },
                  { :denomination => 1.00, :amount => 5 },
                  { :denomination => 2.00, :amount => 5 }
        ]

当用户进行购买时,他们使用的硬币以数组的形式作为参数传入。


    def pay(coins = [])
        coins.each do |coin|
          coin = @change.find { |x| x[:denomination] == coin }
          coin[:amount] += 1
        end
     end

当我从终端手动测试 irb 时,这可以正常工作。但是,当我尝试运行 rspec 时,它会失败并出现 NoMethodError:

 Failure/Error: coin[:amount] += 1

     NoMethodError:
       undefined method `[]' for nil:NilClass

我真的不明白为什么这会因 NoMethodError 而失败,尤其是当它在终端中运行良好时。有人可以帮忙吗?

谢谢:)

更新:

规格:

describe '#pay' do
    it 'updates the change' do
      till.pay([0.5])
      expect(till.change).to include(:denomination=>0.5, :amount=>6)
    end
  end

【问题讨论】:

  • 您的规范中的代码是什么?
  • @SebastianPalma 我已更新以添加到规范中 :)
  • pay 中,coin 参数是浮点数数组,浮点数没有实现[] 方法,因此出现错误。您能否进一步解释您正在尝试做什么,以便更好地理解和获得更好的答案?
  • 我明白了,但是当我在终端中对此进行功能测试时,它是如何工作的,看起来很奇怪?当然 - 当用户用硬币支付物品(本例中为 50p 硬币,即 0.5)时,我希望将其添加到收银机中已经存在的钱中(第一个哈希)。这有意义吗?
  • 抱歉,最后一条评论并不是真正的问题,我混淆了错误,因为每个评论中都有硬币阴影变量。看看提供的答案。

标签: ruby hash


【解决方案1】:

nil 对象中调用 [] 时出现错误。

当您传递不在您的 @change 实例变量中的面额时,@change.find { ... } 的结果将是 nil,并且在 nil 对象中调用 [] 将引发 NoMethodError。

这是您尚未涵盖的情况,可以通过在 each 枚举器中使用 next 来解决:

coins.each do |coin|
  current_denomination = @change.find { |x| x[:denomination] == coin }
  next unless current_denomination

  current_denomination[:amount] += 1
end

p pay([0.05, 0.05, 2.0, 100])

如果相同硬币和面额没有找零,您只需“跳转”到硬币中的下一个元素。

请注意,@change 的当前实现允许您在数组中重复哈希,如果面额与其初始值金额不同,这可能会导致错误。您可以只使用哈希将面额和金额都存储为键值:

p [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0].to_h { |a| [a, 5] }
# {0.01=>5, 0.02=>5, 0.05=>5, 0.1=>5, 0.2=>5, 0.5=>5, 1.0=>5, 2.0=>5}

那是 Ruby 2.6+。


作为对您的实现的一个小调整,您可以将 @change 移动到一个单独的方法中,然后您可以通过在其上调用 tap 来调用和使用该方法:

DENOMINATIONS = [0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 2.0].freeze
HASH_KEYS = %i[denomination amount].freeze

def initial_coins
  DENOMINATIONS.zip(Array.new(8, 5)).map { |coin| HASH_KEYS.zip(coin).to_h }
end

def pay(coins = [])
  initial_coins.tap do |this|
    coins.each do |coin|
      current_denomination = this.find { |initial_coin| initial_coin[:denomination] == coin }
      next unless current_denomination

      current_denomination[:amount] += 1
    end
  end
end

pp pay([0.05, 0.05, 2.0, 100])
# [{:denomination=>0.01, :amount=>5},
#  {:denomination=>0.02, :amount=>5},
#  {:denomination=>0.05, :amount=>7},
#  {:denomination=>0.1, :amount=>5},
#  {:denomination=>0.2, :amount=>5},
#  {:denomination=>0.5, :amount=>5},
#  {:denomination=>1.0, :amount=>5},
#  {:denomination=>2.0, :amount=>6}]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-09
    • 2016-05-24
    • 1970-01-01
    • 2013-04-18
    • 2012-12-13
    • 1970-01-01
    • 2015-04-28
    • 2015-03-09
    相关资源
    最近更新 更多