这是另一种需要较少数据来构建散列的方法。例如,如果行
A = B = C = D
存在,不需要以下任何一种:
A = B
A = B = C
行的顺序并不重要。
代码
def hashify(str)
str.lines.each_with_object({}) { |line, h|
line.split(/\s*=\s*/).reduce(h) { |g,w|
(w[-1] == "\n") ? g[w.chomp] = nil : g[w] ||= {} } }
end
示例
str =<<_
A = B = C
G = I
A = B = D
A = E = F
G = H
A = K
G = J
_
hashify(str)
#=> {"A"=>{"B"=>{"C"=>nil, "D"=>nil}, "E"=>{"F"=>nil}, "K"=>nil},
# "G"=>{"I"=>nil, "H"=>nil, "J"=>nil}}
说明
以上str:
a = str.lines
#=> ["A = B = C\n", "A = B = D\n", "A = E = F\n",
# "G = H\n", "G = I\n", "G = J\n"]
请注意,String#lines 与 split(/'\n'/) 不同,它保留换行符。将它们保留在这一点上是有意的。它们有一个重要的用途,如下所示。
enum = a.each_with_object({})
#=> #<Enumerator: ["A = B = C\n", "A = B = D\n", "A = E = F\n", "G = H\n",
# "G = I\n", "G = J\n"]:each_with_object({})>
我们可以将枚举器转换为数组,以查看Array#each 将传递给块的元素:
enum.to_a
#=> [["A = B = C\n", {}], ["A = B = D\n", {}], ["A = E = F\n", {}],
# ["G = H\n", {}], ["G = I\n", {}], ["G = J\n", {}]]
enum 现在调用 each 将每个元素传递到块中:
enum.each { |line, h| line.split(/\s*=\s*/).reduce(h) { |g,w|
(w[-1] == '\n') ? g[w.chomp] = nil : g[w] ||= {} } }
#=> {"A"=>{"B"=>{"C\n"=>{}, "D\n"=>{}}, "E"=>{"F\n"=>{}}},
# "G"=>{"H\n"=>{}, "I\n"=>{}, "J\n"=>{}}}
Array#each 传入块的第一个值是:
["A = B = C\n", {}]
将其分解或“消除歧义”为两个元素并分配给块变量:
line = "A = B = C\n"
h = {}
我们现在执行块中的代码:
b = line.split(/\s*=\s*/)
#=> ["A", "B", "C\n"]
b.reduce(h) { |g,w|
(w[-1] == '\n') ? g[w.chomp] = nil : g[w] ||= {} }
#=> {}
reduce 的初始值是我们正在构建的哈希 h,最初是空的。当h 和"A" 被传入块时,
g = h #=> {}
w = "A"
所以(注意"\n" 需要双引号)
w[-1] == "\n"
#=> "A" == '\n'
#=> false
所以我们执行
g[w] ||= {}
#=> g['A'] ||= {}
#=> g['A'] = g['A'] || {}
#=> g['A'] = nil || {}
#=> {}
现在
h #=> {"A"=>{}}
g[w] => {} 然后被传回reduce 并且传递给块的第二个元素的块变量是:
g = g["A"] #=> {}
w = "B"
自从
w[-1] == "\n" #=> false
我们再次执行
g[w] ||= {}
#=> g["B"] ||=> {} => {}
现在
h #=> {"A"=>{"B"=>{}}}
最后,[g["B"], "C\n"] 被传递到块中,分解并分配给块变量:
g = g["B"] #=> {}
w = "C\n"
但w 中出现换行符会导致
w[-1] == "\n" #=> true
告诉我们这是行中的最后一个单词,所以我们需要去掉换行符并将值设置为nil:
g[w.chomp] = nil
#=> g["C"] = nil
导致:
h #=> {"A"=>{"B"=>{"C"=>nil}}}
在字符串中保留换行符提供了所需的“标志”,用于以不同于其他方式处理每行上的最后一个单词。
其他行类似处理。