gpt4 book ai didi

ruby - 传递给 Hash.new 的 block 或对象何时创建或运行?

转载 作者:数据小太阳 更新时间:2023-10-29 08:30:21 24 4
gpt4 key购买 nike

我正在研究 ruby​​ koans,我在理解这段代码何时运行时遇到了一些困难:

hash = Hash.new {|hash, key| hash[key] = [] }

如果散列中没有值,新数组何时分配给散列中的给定键?第一次访问哈希值而不先分配它时会发生这种情况吗?请帮助我了解何时为任何给定的哈希键创建准确的默认值。

最佳答案

为了那些刚接触 Ruby 的人的利益,我已经讨论了解决该问题的替代方法,包括作为这个问题的实质的方法。

任务

假设给你一个数组

arr = [[:dog, "fido"], [:car, "audi"], [:cat, "lucy"], [:dog, "diva"], [:cat, "bo"]]  

并希望创建散列

{ :dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"] }

第一个解决方案

h = {}
arr.each do |k,v|
h[k] = [] unless h.key?(k)
h[k] << v
end
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]}

这很简单。

第二种方案

更像Ruby的是这样写:

h = {}
arr.each { |k,v| (h[k] ||= []) << v }
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]}

当 Ruby 看到 (h[k] ||= []) << v她做的第一件事就是将其扩展到

(h[k] = h[k] || []) << v

如果h没有 key k , h[k] #=> nil , 所以表达式变成

(h[k] = nil || []) << v

变成

(h[k] = []) << v

所以

h[k] #=> [v]

请注意 h[k]在等式左边使用方法 Hash#[]= ,而 h[k]右边的员工 Hash#[] .

此解决方案要求所有哈希值都不等于 nil .

第三种方案

第三种方法是给散列一个默认值。如果哈希 h没有 key k , h[k]返回默认值。有两种类型的默认值。

将默认值作为参数传递给 Hash::new

如果将空数组作为参数传递给 Hash::new ,该值成为默认值:

a = []
a.object_id
#=> 70339916855860
g = Hash.new(a)
#=> {}

g[k]返回 []什么时候h没有 key k . (然而,散列没有改变。)这个结构有重要的用途,但在这里不合适。要了解原因,假设我们写

x = g[:cat] << "bo"
#=> ["bo"]
y = g[:dog] << "diva"
#=> ["bo", "diva"]
x #=> ["bo", "diva"]

这是因为 :cat 的值和 :dog都设置为同一个对象,一个空数组。我们可以通过检查 object_id 看到这一点小号:

x.object_id
#=> 70339916855860
y.object_id
#=> 70339916855860

给予 Hash::new返回默认值的 block

默认值的第二种形式是执行分块计算。如果我们用一个 block 定义散列:

h = Hash.new { |h,k| h[key] = [] }

那么如果h没有 key k , h[k]将设置为等于 block 返回的值,在本例中为空数组。请注意, block 变量 h是新创建的空哈希。这允许我们写

h = Hash.new { |h,k| h[k] = [] }
arr.each { |k,v| h[k] << v }
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]}

因为传递给 block 的第一个元素是arr.first , block 变量通过评估赋值

k, v = arr.first
#=> [:dog, "fido"]
k #=> :dog
v #=> "fido"

block 计算因此

h[k] << v
#=> h[:dog] << "fido"

但是自h (还)没有 key :dog , block 被触发,设置 h[k]等于 []然后那个空数组附加了“fido”,这样

h #=> { :dog=>["fido"] }

类似地,在 arr 的下两个元素之后被传递到我们拥有的区 block

h #=> { :dog=>["fido"], :car=>["audi"], :cat=>["lucy"] }

arr 的下一个(第四个)元素时传递给 block ,我们评估

h[:dog] << "diva"

但现在h确实有一个 key ,所以默认值不适用,我们最终得到

h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy"]} 

arr 的最后一个元素类似处理。

请注意,当将 Hash::new 与 block 一起使用时,我们可以这样写:

h = Hash.new { launch_missiles("any time now") }

在这种情况下h[k]将被设置为等于 launch_missiles 的返回值.换句话说,在 block 中可以做任何事情。

更像 Ruby

最后,更像Ruby的写法

h = Hash.new { |h,k| h[k] = [] }
arr.each { |k,v| h[k] << v }
h #=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]}

就是使用Enumerable#each_with_object :

arr.each_with_object(Hash.new { |h,k| h[k] = [] }) { |k,v| h[k] << v }
#=> {:dog=>["fido", "diva"], :car=>["audi"], :cat=>["lucy", "bo"]}

这消除了两行代码。

哪个最好?

就我个人而言,我对第二种和第三种方案无动于衷。两者都在实践中使用。

关于ruby - 传递给 Hash.new 的 block 或对象何时创建或运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40795510/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com