我正在尝试为哈希创建merge
方法的版本。这是测试之一:
test_hash_1 = { a: 10, b: 2 }
test_hash_2 = { a: 5, b: 10, c: 3 }
test_hash_1.my_merge(test_hash_2) { |key, oldval, newval| newval - oldval } #=> {a: -5, b: 8, c: 3}
类似于Hash#merge
,代码需要返回一个特定键的所有值的数组。例如:
test_hash_1 = { a: 10, b: 2 }
test_hash_2 = { b: 3, c: 4 }
expect { |b| test_hash_1.my_merge(test_hash_2, &b)}.to yield_successive_args([:b, 2, 3])
这就是我所拥有的:
def my_merge(hash2, &blk)
new_hash = self
if block_given?
hash2.each do |k1, v1|
new_hash[k1] = blk.call
end
else
hash2.each do |k2, v2|
new_hash[k2] = v2
end
end
new_hash
end
end
我很难理解积木的工作原理。我的代码与预期结果不符。我将不胜感激。
答案 0 :(得分:5)
让我们看一下您的代码:
def my_merge(hash2, &blk)
# ...
end
由于您不会传递该块,因此不必显式指定block参数。您可以将其定义为:
def my_merge(hash2)
# ...
end
并使用yield(...)
代替blk.call(...)
。
您通过以下方式创建new_hash
:
new_hash = self
,它将使new_hash[k1] = ...
等效于self[k1] = ...
。为避免修改接收者,请改为通过dup
创建一个副本:
new_hash = dup
您的第一个条件检查是否给出了块。但是根据文档,仅对重复条目调用该块。因此实际的条件是:密钥是否存在 是给定的块。并且由于必须考虑密钥,因此我们必须将其移至each
块中:
hash2.each do |k, v|
if new_hash.key?(k) && block_given?
new_hash[k] = yield(k, new_hash[k], v)
else
new_hash[k] = v
end
end
我们通过yield
传递的3个参数是键,旧值和新值。
您可能已经注意到代码中的模式:
def m(ary)
obj = initial_value
ary.each do |e|
# modify obj
end
obj
end
可以使用each_with_object
将其表达得更加简洁:
def m(ary)
ary.each_with_object(initial_value) do |e, o|
# modify obj
end
end
整个代码:
class Hash
def my_merge(hash)
hash.each_with_object(dup) do |(k, v), h|
if h.key?(k) && block_given?
h[k] = yield(k, h[k], v)
else
h[k] = v
end
end
end
end
答案 1 :(得分:1)
有关要求,请参见Hash#merge。
class Hash
def my_merge(h)
keys.each_with_object({}) do |k,g|
g[k] = if h.key?(k)
block_given? ? yield(k, self[k], h[k]) : h[k]
else
self[k]
end
end.tap { |g| (h.keys-keys).each { |k| g[k] = h[k] } }
end
end
h = { a: 1, b: 2, c: 3 }
g = { b: 3, c: 4, d: 5 }
h.my_merge(g) #=> {:a=>1, :b=>3, :c=>4, :d=>5}
h.merge(g) #=> {:a=>1, :b=>3, :c=>4, :d=>5}
h.my_merge(g) { |_,o,n| o+n } #=> {:a=>1, :b=>5, :c=>7, :d=>5}
h.merge(g) { |_,o,n| o+n } #=> {:a=>1, :b=>5, :c=>7, :d=>5}
merge
不返回公用键的值的数组,但可以与特定的块一起使用来完成此操作:
h.my_merge(g) { |_,o,v| [o, v] } #=> {:a=>1, :b=>[2, 3], :c=>[3, 4], :d=>5}
h.merge(g) { |_,o,v| [o, v] } #=> {:a=>1, :b=>[2, 3], :c=>[3, 4], :d=>5}
对于不熟悉Object#tap的读者,如果没有它,我将需要编写类似于以下内容的方法。
def my_merge(h)
g = keys.each_with_object({}) do |k,g|
g[k] = if h.key?(k)
block_given? ? yield(k, self[k], h[k]) : h[k]
else
self[k]
end
end
(h.keys-keys).each { |k| g[k] = h[k] }
g
end