如何修改由 Frozen String Literals 组成的 Ruby 数组的元素

How to modify elements of Ruby Array composed of Frozen String Literals

给定一个文件

# frozen_string_literal: true

alphabet = %w(a b c)
alphabet.each do |letter|
  letter << 'suffix'
end

如果数组的各个元素默认是冻结的字符串文字,如何修改数组的各个元素?有没有办法让 letter 可变?我试着在每个块中添加一个一元 +,如下所示:alphabet.each do |+letter|。有没有更好的方法来解决这个问题?

无法解冻冻结的对象。但是您可以从中构建新对象。

# frozen_string_literal: true

alphabet = %w(a b c)
alphabet = alphabet.map do |letter|
  letter + 'suffix'
end

无法解冻对象。这首先会破坏冻结它的意义。

我要问自己的第一个问题是:如果您需要修改这些字符串,为什么要先冻结它们?这是没有意义的。如果您绝对必须修改它们,请不要冻结它们。

但是,无论如何我都不是突变的忠实拥护者,所以我建议改用不可变的解决方案:

alphabet.map { |letter| letter + 'suffix' }

如果你坚持使用 mutation 并且无法将这些字符串的创建更改为不被冻结,那么 Ruby 为你提供 String#+@ 方法 returns a non-frozen 复制 如果字符串被冻结,如果不是则只复制字符串本身。

所以,你可以做类似的事情

alphabet.each do |letter|
  +letter << 'suffix'
end

除非那是行不通的,因为您创建了字符串的 副本 并修改了该副本,但您没有对副本做任何事情,也没有返回它,不将其存储在任何地方,不将其分配给任何东西,不将其传递给任何方法。它只会消失。

你可以做到

alphabet.map do |letter|
  +letter << 'suffix'
end

但同样,您不是在修改数组中的字符串而是创建新的字符串,您是在创建一个新数组,那么创建可变副本的意义何在?

你可以做到

alphabet.map! do |letter|
  +letter << 'suffix'
end

这会改变 数组 而不是其中的字符串。

要获得与从可变字符串中获得的语​​义相同的语义是完全不可能的,除非很好,字符串从一开始就是可变的(duh!)

因此,如果您要以这样的方式开始您的程序:

alphabet.map!(&:+@)

然后它将按照您想要的方式工作:

#!/usr/bin/env ruby

# frozen_string_literal: true

alphabet = %w(a b c)
alphabet.map!(&:+@)

alphabet.each do |letter|
  letter << 'suffix'
end

alphabet
#=> %w[asuffix bsuffix csuffix]

但如果可能的话,我总是更喜欢不可变的、纯函数式的、引用透明的、完整的解决方案。