所以map
与map!
foo = [1,2,3]
foo.map { |i| i*=2}
=> [2, 4, 6]
foo
=> [1, 2, 3] # foo unchanged
foo.map! { |i| i*=2}
=> [2, 4, 6]
foo
=> [2, 4, 6] # foo is changed
一切正常/预期。现在使用hash
数组,让我们来做map
:
bar = [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
=> [{:foo=>1, :bar=>11}, {:foo=>2, :bar=>12}, {:foo=>3, :bar=>13}]
bar.map { |i| i[:foo]*= 2}
=> [2, 4, 6]
bar
=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}] # changed
因此,似乎使用map
修改了哈希的基础数组,并且基本上与map!
相同:
bar.map! do |i|
i[:foo]*=2
i
end
=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
bar
=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
这里可能缺少一些基本知识。不寻找替代品,只是试图了解什么似乎是未记录的(?)注释/陷阱/不一致。 Tnx!
答案 0 :(得分:2)
在第一个示例中,我们比较i
的值:
foo = [1,2,3]
foo.map { |i| i*=2}
i
这是一个数字。数字是不可变的。因此,在块中写入i * 2
或i *= 2
没有区别。无论您使用map
还是map!
,分配都不会在任何地方进行。
即使您使用map!
,为什么分配也不重要?我们可以快速重新实现map!
来了解:
def my_map!(list, &blk)
list.each_index { |i| list[i] = blk.call(list[i]) }
list
end
如您所见,我们将每个索引的值设置为块的返回值。并且i * 2
或i *= 2
的返回值是相同的。
现在,看第二个例子:
bar = [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
bar.map { |i| i[:foo]*= 2}
为什么这会改变哈希值? i
的值是一个散列,是可变的。分配键值(i[:foo]*= 2
)会使它变异,无论它是否发生在each
,map
,map!
等中。
因此,为了追逐现实,您需要使用merge
或dup
之类的东西来创建新的哈希:
bar = [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
bar.map { |i| i.merge(foo: i[:foo] * 2) }
答案 1 :(得分:2)
map
方法不会更改数组的内容,这可以通过检查数组每个元素的object_id
来看出。但是,每个元素都是一个可变的哈希,因此允许更新哈希的内容。从以下结果的逐步跟踪中可以看出:
p baz = [{foo:1,bar:11}, {foo:2,bar:12}, {foo:3,bar:13}]
puts "baz contents IDs are:"
baz.each { |hsh| p hsh.object_id }
puts "performing the map operation"
p baz.map { |i| i[:foo] *= 2 }
puts "baz contents IDs still are:"
baz.each { |hsh| p hsh.object_id }
puts "...but the contents of those contents have changed:"
p baz
产生例如:
[{:foo=>1, :bar=>11}, {:foo=>2, :bar=>12}, {:foo=>3, :bar=>13}]
baz contents IDs are:
70261047089900
70261047089860
70261047089840
performing the map operation:
[2, 4, 6]
baz contents IDs still are:
70261047089900
70261047089860
70261047089840
...but the contents of those contents have changed:
[{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
请注意,我将数组的名称更改为baz
,以避免由于名称阴影而引起的混乱。
答案 2 :(得分:1)
我想加两分钱。
当您在块中使用完全相同的代码(e[:foo] *= 2
)时,您会看到map
和map!
不同。
map
bar = [ {foo:1, bar:11}, {foo:2, bar:12}, {foo:3, bar:13} ]
bar.map { |e| e[:foo] *= 2 } #=> [2, 4, 6]
bar #=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
在bar
上,您获得与使用each
完全相同的效果,除了调用的返回值:
bar = [ {foo:1, bar:11}, {foo:2, bar:12}, {foo:3, bar:13} ]
bar.each { |e| e[:foo] *= 2 } #=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
bar #=> [{:foo=>2, :bar=>11}, {:foo=>4, :bar=>12}, {:foo=>6, :bar=>13}]
map!
bar = [ {foo:1, bar:11}, {foo:2, bar:12}, {foo:3, bar:13} ]
bar.map! { |e| e[:foo] *= 2 } #=> [2, 4, 6]
bar #=> [2, 4, 6]