为什么是str.next!在一个Enumerable#map中用相同的元素填充数组?

时间:2019-07-13 10:56:42

标签: arrays ruby hashtable

Enumerable#map创建一个带有返回值的数组,产生后在块中。

在这种情况下,说:

v = 'a'
26.times.map { |i| v.ord.+(i).chr }

# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

但是为什么下面的代码用相同的元素填充数组?

v = '`'
26.times.map { v.next! }

# => ["z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z"]
v = '`'
Array.new(26) { v.next! }

# => ["z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z"]

难道它们都不都有a到z的元素吗?

同样,这可行:

v = '`'
Array.new(26) { v = v.succ }

# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

实际上我正在尝试:

v = "\xf0\x9d\x93\xa9"
('a'..'z').reduce({}) { |h, i| h.merge(i.intern => v = v.succ) }

# => {:a=>"?", :b=>"?", :c=>"?", :d=>"?", :e=>"?", :f=>"?", :g=>"?", :h=>"?", :i=>"?", :j=>"?", :k=>"?", :l=>"?", :m=>"?", :n=>"?", :o=>"?", :p=>"?", :q=>"?", :r=>"?", :s=>"?", :t=>"?", :u=>"?", :v=>"?", :w=>"?", :x=>"?", :y=>"?", :z=>"?"}

但是我在使用succ! / next!

时得到所有z
v = "\xf0\x9d\x93\xa9"
('a'..'z').reduce({}) { |h, i| h.merge(i.intern => v.succ!) }

# => {:a=>"?", :b=>"?", :c=>"?", :d=>"?", :e=>"?", :f=>"?", :g=>"?", :h=>"?", :i=>"?", :j=>"?", :k=>"?", :l=>"?", :m=>"?", :n=>"?", :o=>"?", :p=>"?", :q=>"?", :r=>"?", :s=>"?", :t=>"?", :u=>"?", :v=>"?", :w=>"?", :x=>"?", :y=>"?", :z=>"?"}

除了succ! / next!不会更改存储位置和object_id,v.succ!v = v.succ是否相同?

1 个答案:

答案 0 :(得分:3)

当您对变量next!调用succ!str时,分配给该变量的对象将发生突变,并返回对该对象的引用。如果str = 'a'并且您呼叫str.next! 26次,则str变为z。每次调用next!时,都会返回对同一对象的引用。结果,您得到一个由26个对同一对象的引用组成的数组。这就是数组中所有元素都相同的原因。

您可以通过检查object_id数组元素来进行测试:

pry(main)> str = 'a'
'a'
pry(main)> array = 3.times.map{ str.next!}
=> ["d", "d", "d"]
pry(main)> array.map(&:object_id)
=> [47056742362940, 47056742362940, 47056742362940]
pry(main)> array.map(&:object_id).uniq
=> [47056742362940]

编辑str时,所有数组元素都会更新:

[39] pry(main)> str << "b"
=> "db"
[40] pry(main)> array
=> ["db", "db", "db"]
[41] pry(main)> str.replace
str.replace
[41] pry(main)> str.replace('a')
=> "a"
[42] pry(main)> array
=> ["a", "a", "a"]

如果要使用整个字母组成的数组,则需要在更改当前字母后复制字符串,如下所示:

[25] pry(main)> str = 'a'
=> "a"
[26] pry(main)> 25.times.map{ str.next!.dup} 
=> ["b",
 "c",
 "d",
 "e",
 "f",
 "g",
 "h",
 "i",
 "j",
 "k",
 "l",
 "m",
 "n",
 "o",
 "p",
 "q",
 "r",
 "s",
 "t",
 "u",
 "w",
 "x",
 "y",
 "z"]

您还可以使用范围:

[32] pry(main)> ('a'..'z').to_a
=> ["a",
 "b",
 "c",
 "d",
 "e",
 "f",
...