【问题标题】:Why does the str.next! in an Enumerable#map fill the array with same elements?为什么str.next!在 Enumerable#map 中用相同的元素填充数组?
【发布时间】:2019-11-22 21:38:27
【问题描述】:

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不一样吗?

【问题讨论】:

  • 关于您的问题:(:a..:z).zip('????'..'????').to_h 似乎更容易。
  • 是的,这很酷!但是为什么 bang 方法不工作而赋值运算符工作呢?
  • 它可以工作,但是你有一个数组引用同一个对象 26 次,而不是一个包含 26 个不同对象的数组。见下文。

标签: arrays ruby hashtable


【解决方案1】:

当您在变量str 上调用next!succ! 时,分配给该变量的对象会发生变化并返回对该对象的引用。如果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",
...

【讨论】:

  • 哦,太棒了!因此,在这种情况下,当您将数组指定为 a 时,如果您编写 z.replace('hello'),则整个数组 a 将填充“你好”! ...类似于a.fill('hello')
  • 我已经更新了我的答案以涵盖更改对象。
猜你喜欢
  • 1970-01-01
  • 2015-10-03
  • 2015-11-21
  • 1970-01-01
  • 1970-01-01
  • 2017-06-19
  • 2020-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多