【问题标题】:Why is only one value in my hash being changed?为什么我的哈希中只有一个值被更改?
【发布时间】:2019-11-17 13:59:53
【问题描述】:

我正在制作一个简单的 RPG 作为一个学习项目,但我遇到了角色创建者的一部分问题。

此代码应确定分配给player[:caste][:skill]player[:sub][:skill] 的技能字符串,然后将player[:skills] 中每个相应技能的值增加2。无论分配给player[:caste][:skill] 和@ 的字符串如何,此代码都应该有效987654325@,只要等于player[:skills].to_s

目前,它仅将更改应用于player[:skills][:endurance],而不是player[:skills][:athletics]

player = {
  caste: {skill: "athletics"},
  sub: {skill: "endurance"},
  skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 0, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0},
}

player[:skills] = player[:skills].map do |skill, mod|
  [skill, (mod += 2 if skill.to_s == player[:caste][:skill])]
  [skill, (mod += 2 if skill.to_s == player[:sub][:skill])]
end.to_h

换句话说,我的代码返回以下player[:skills] 哈希:

skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 2, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0}

但我希望它返回:

skills: {acrobatics: 0, athletics: 2, engineering: 0, endurance: 2, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0}

如果有更简单的方法,请告诉我。我还尝试了以下方法:

player[:skills] = player[:skills].map do |skill, mod|
  [skill, (mod += 2 if skill.to_s == (player[:caste][:skill] || player[:sub][:skill]))]
end.to_h

只影响player[:caste][:skill]中的技能。

【问题讨论】:

  • 不相关,但将所有内容保存在哈希中可能不是最好的方法。将这样的行为封装到类中并避免散布在代码中的任意数量的条件可能是一个好主意。
  • 谢谢,戴夫。我会牢记这一点。在这个项目中,我主要将数据存储在哈希和数组中,以便更好地使用它们。在未来的项目中,我将更多地关注类。
  • 不要将尾随 if 嵌入到数组中。很难破译你在做什么,如果测试失败会插入 nil 导致你以后必须防止的不一致的数据类型。

标签: ruby ruby-hash


【解决方案1】:

当我运行你的代码时,我得到了这个结果。

{:acrobatics=>nil, :athletics=>nil, :engineering=>nil, :endurance=>2, :heal=>nil, :history=>nil, :influence=>nil, :insight=>nil, :magicka=>nil, :perception=>nil, :riding=>nil, :stealth=>nil, :streetwise=>nil, :thievery=>nil}

那是因为 map 返回最后执行的语句。此外,您实际上只在与子技能匹配时才为技能设置一个值,否则,它被设置为 nil。

因此,您的代码中发生的情况是,每次迭代都返回以下内容,这是传递给 map 的块中最后一条语句的结果。

[:acrobatics, nil]
[:athletics, nil]
[:engineering, nil]
[:endurance, 2]
[:heal, nil]
[:history, nil]
[:influence, nil]
[:insight, nil]
[:magicka, nil]
[:perception, nil]
[:riding, nil]
[:stealth, nil]
[:streetwise, nil]
[:thievery, nil]

最终结果是一个如下所示的数组。

[[:acrobatics, nil], [:athletics, nil], [:engineering, nil], [:endurance, 2], [:heal, nil], [:history, nil], [:influence, nil], [:insight, nil], [:magicka, nil], [:perception, nil], [:riding, nil], [:stealth, nil], [:streetwise, nil], [:thievery, nil]]

最终映射到一个新的哈希

{:acrobatics=>nil, :athletics=>nil, :engineering=>nil, :endurance=>2, :heal=>nil, :history=>nil, :influence=>nil, :insight=>nil, :magicka=>nil, :perception=>nil, :riding=>nil, :stealth=>nil, :streetwise=>nil, :thievery=>nil}

您得到所有​​这些 nil 的原因是因为在您的陈述中,如果 if 陈述不正确,则案例的结果是 nil。 例如:

[skill (mod += 2 if skill.to_s == player[:caste][:skill])]

如果 skill.to_s == player[:caste][:skill] 不正确,将返回 [the_skill, nil]

要查看发生了什么,请在 irb 中尝试此操作。

x = 0
=> 0
x += 1 if false
=> nil
x += 1 if true
=> 1 

你可以使用这样的东西来克服这个问题。

[skill, skill.to_s == player[:caste][:skill] ? mod + 2 : mod ]

或者使用上面的例子:

x = 0
=> 0
x =  false ? x + 1 : x 
=> 0
x =  true ? x + 1 : x
=> 1

您的代码的以下修改版本应该可以工作。

player[:skills] = player[:skills].map do |skill, mod|
  [skill, skill.to_s == player[:caste][:skill] || skill.to_s == player[:sub][:skill] ? mod + 2 : mod ]
end.to_h

但是,这里稍微有点冗长,但希望更容易遵循的方式来完成您想要做的事情,并允许在将来添加修改而不会让代码变得太混乱。

player = {
  caste: {skill: "athletics"},
  sub: {skill: "endurance"},
  skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 0, heal: 0, history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, stealth: 0, streetwise: 0, thievery: 0},
}

player_caste_skill = player[:caste][:skill]
player_sub_skill = player[:sub][:skill]
current_skills = player[:skills]
updated_skills = {}
current_skills.each_pair do |skill, prev_value| 
  new_value = prev_value 
  case skill.to_s
    when player_caste_skill, player_sub_skill
      new_value = prev_value + 2
    when "some_other_skill"  
      new_value = prev_value + 3
  end
  updated_skills[skill] = new_value
end
puts current_skills
puts updated_skills

【讨论】:

  • 感谢深思熟虑的解释。我能够遵循这两个示例,但是您使用三元语句的第一个示例不考虑 player[:sub][:skill]。我尝试了一些变化:[skill, skill.to_s == (player[:caste][:skill] || player[:sub][:skill]) ? mod + 2 : mod],但没有成功。你有什么建议吗?如果可能的话,我想这样做。
  • 我添加了一个你想要的部分(至少我认为它是)。最后它与我最后发布的并没有太大不同,但更难理解
【解决方案2】:

我将默认值 (Hash#default) 设置为 player[:skill] 哈希,只是为了避免在缺少密钥的情况下出错(它添加了密钥!!),允许在不需要的情况下添加新密钥将每个技能初始化为 0。

player[:skills].default = 0

然后在一行中扫描您需要递增的键:

[:caste, :sub].each { |key| player.dig(key, :skill).to_sym.then { |skill| player[:skills][skill] += 2 } }


由于初始化,您的播放器也可以
player = {
  caste: {skill: "athletics"},
  sub: {skill: "endurance"},
  skills: {}
}

返回如下结果:

player #=> {:caste=>{:skill=>"athletics"}, :sub=>{:skill=>"endurance"}, :skills=>{:athletics=>2, :endurance=>2}}

地点:

player[:skills][:whatever] #=> 0

【讨论】:

  • 考虑在增量后清空:caste:sub 哈希。我认为 :caste:sub 只有一个元素或它们的值不是数组,否则你需要循环它们。
  • 这里有很多我不知道的事情。也真的很紧凑。谢谢,iGian。
【解决方案3】:

我会迭代定义的技能而不是技能值。

player.
  map { |_, h| h[:skill] }.
  compact.
  map(&:to_sym).
  each { |skill| player[:skills][skill] += 2 }

现在player 已相应更新,您可以通过检查playerp player 或类似方法进行检查。

【讨论】:

  • 谢谢,阿列克谢。在您的示例中,句点 (.) 的功能是什么?我以前没见过这个。
【解决方案4】:

把代码改成这样:

player = {
 caste: {skill: "athletics"},
 sub: {skill: "endurance"},
 skills: {acrobatics: 0, athletics: 0, engineering: 0, endurance: 0, heal: 0, 
 history: 0, influence: 0, insight: 0, magicka: 0, perception: 0, riding: 0, 
 stealth: 0, streetwise: 0, thievery: 0},
}

player[:skills] = player[:skills].map do |skill, mod|
  [skill, (mod += 2 if [player[:caste][:skill], player[:sub][:skill]].include? 
  (skill.to_s))]
end.to_h

您的代码不起作用的原因是 map 返回最后一行作为当前迭代的结果,所以在athletics 的情况下,最后一行是

[skill, (mod += 2 if skill.to_s == player[:sub][:skill])]

将是 false,即 nil,这就是为什么只有 endurance 案例有效。

希望对你有帮助。

【讨论】:

  • 谢谢 Amr。这很有帮助。有没有办法在不将player[:skills] 中的所有其他值设置为nil 的情况下做到这一点?我希望每个未受影响的值都保持等于 0
  • 我尝试了以下方法:player[:skills] = player[:skills].map { |skill, mod| [skill, ([player[:caste], player[:sub][:skill]].include?(skill.to_s) ? mod += 2 : mod = 0)] }.to_h 导致与以前相同的问题,但设法将每个未受影响的值设置为 0
  • 您的代码很好,您只是忘记将其写为player[:caste][:skill],而是将其写为player[:caste],这就是它没有为您提供所需输出的原因。抱歉@Richard 回复晚了
猜你喜欢
  • 2011-08-03
  • 2022-01-15
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多