为什么我的哈希中只有一个值被更改?



我正在制作一个简单的RPG作为学习项目,并且与部分角色创建器有问题。

此代码应确定分配给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

换句话说,我的代码返回以下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]中找到的技能。

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

{: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])]

将返回[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

我会为player[:skill]哈希设置一个默认值(Hash#default),只是为了避免在缺少键的情况下出错(它添加了键!!),允许添加一个新键,而无需初始化为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

将代码更改为如下所示:

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

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

希望对您有所帮助。

最新更新