为什么我的散列中只有一个值被更改?

Why is only one value in my hash being changed?

我正在制作一个简单的角色扮演游戏作为学习项目,但我对部分角色创建器有疑问。

此代码应确定分配给 player[:caste][:skill]player[:sub][:skill] 的技能字符串,然后将 player[:skills] 中的每个技能值增加 2。无论什么字符串,此代码都应该有效赋值给player[:caste][:skill]player[:sub][:skill],只要等于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

换句话说,我的代码是 returning 以下 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}

但我想要 return:

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] 中的技能。

将代码更改为如下所示:

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

你的代码不起作用的原因是映射 return 最后一行作为当前迭代的结果,所以在 athletics 的情况下,最后一行是

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

将是错误的,这将是 nil 这就是为什么只有 endurance 案例有效。

希望对您有所帮助。

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

{: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 return 执行了最后一条语句。另外,你实际上只在匹配子技能时为技能设置一个值,否则设置为零。

所以在您的代码中发生的事情是,每次迭代都是 returning 以下内容,这是传递到 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])]

将 return [the_skill, nil] 的情况 skill.to_s == player[:caste][:skill] 是不正确的

要查看发生了什么,请在 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

我将默认值 (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

我会遍历定义的技能而不是技能值。

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

现在 player 已相应更新,您可以通过用 p player 或类似方法检查 player 来检查。