冻结早期 Ruby 版本中的字符串文字
Freezing string literals in early Ruby versions
这个问题特别适用于 Ruby 1.9 和 2.1,其中不能自动冻结字符串文字。特别是我指的是 this article,它建议冻结字符串,这样代码的重复计算就不会每次都创建一个新的 String 对象,这在其他优点中据说可以使程序性能更好。作为一个具体的例子,这篇文章提出了表达式
("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)
我想在我们的项目中使用这个概念,出于测试目的,我尝试了以下代码:
3.times { x="abc".freeze; puts x.object_id }
在Ruby 2.3 中,每次都打印相同的对象ID。在 JRuby 1.7 中,在语言级别上对应于 Ruby 1.9,它打印三个不同的对象 ID,尽管我已经明确地冻结了字符串。
有人可以解释一下这是什么原因,以及如何在这种情况下正确使用 freeze
?
In particular I am refering to this article, which suggests to freeze strings, so that repeated evaluation of the code does not create a new String object every time
那不是 Object#freeze
所做的。顾名思义,它 "freezes" 对象,即它不允许对对象的内部状态进行任何进一步的修改。文档中没有任何内容甚至远程暗示 Object#freeze
执行某种重复数据删除或实习。
您可能正在考虑String#-@
,但这在Ruby 2.1 中不存在。它只是在 Ruby 2.3 中添加的,并且实际上在当时具有不同的语义:
Ruby 2.3–2.4: returns self
如果 self
已经冻结,否则 returns self.dup.freeze
, 即 a frozen duplicate of the string:
-str
→ str
(frozen)
If the string is frozen, then return the string itself.
If the string is not frozen, then duplicate the string freeze it and return it.
Ruby 2.5+: returns self
如果 self
已经冻结,否则 returns 字符串的冻结版本that is de-duplicated(即可以在现有冻结字符串的缓存中查找并返回现有版本):
-str
→ str
(frozen)
Returns a frozen, possibly pre-existing copy of the string.
The string will be deduplicated as long as it is not tainted, or has any instance variables set on it.
因此,您链接到的文章在三个方面是错误的:
- 去重只针对字符串,不针对任意对象。
freeze
未执行重复数据删除。
- 重复数据删除仅由
String#-@
从 Ruby 2.5 开始执行。
那篇文章还有第四个说法是错误的,尽管我们不能真的为此责怪作者,因为这篇文章是 2016 年的,而决定是在 2019 年才改变的:Ruby 3.0 will not have immutable string literals by default。
在那篇文章中 是 正确的一件事是 # frozen_string_literal: true
pragma(或相应的命令行选项 --enable-frozen-string-literal
)不仅会冻结所有静态字符串文字,它也会删除它们。
这个问题特别适用于 Ruby 1.9 和 2.1,其中不能自动冻结字符串文字。特别是我指的是 this article,它建议冻结字符串,这样代码的重复计算就不会每次都创建一个新的 String 对象,这在其他优点中据说可以使程序性能更好。作为一个具体的例子,这篇文章提出了表达式
("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)
我想在我们的项目中使用这个概念,出于测试目的,我尝试了以下代码:
3.times { x="abc".freeze; puts x.object_id }
在Ruby 2.3 中,每次都打印相同的对象ID。在 JRuby 1.7 中,在语言级别上对应于 Ruby 1.9,它打印三个不同的对象 ID,尽管我已经明确地冻结了字符串。
有人可以解释一下这是什么原因,以及如何在这种情况下正确使用 freeze
?
In particular I am refering to this article, which suggests to freeze strings, so that repeated evaluation of the code does not create a new String object every time
那不是 Object#freeze
所做的。顾名思义,它 "freezes" 对象,即它不允许对对象的内部状态进行任何进一步的修改。文档中没有任何内容甚至远程暗示 Object#freeze
执行某种重复数据删除或实习。
您可能正在考虑String#-@
,但这在Ruby 2.1 中不存在。它只是在 Ruby 2.3 中添加的,并且实际上在当时具有不同的语义:
Ruby 2.3–2.4: returns
self
如果self
已经冻结,否则 returnsself.dup.freeze
, 即 a frozen duplicate of the string:-str
→str
(frozen)If the string is frozen, then return the string itself.
If the string is not frozen, then duplicate the string freeze it and return it.Ruby 2.5+: returns
self
如果self
已经冻结,否则 returns 字符串的冻结版本that is de-duplicated(即可以在现有冻结字符串的缓存中查找并返回现有版本):-str
→str
(frozen)Returns a frozen, possibly pre-existing copy of the string.
The string will be deduplicated as long as it is not tainted, or has any instance variables set on it.
因此,您链接到的文章在三个方面是错误的:
- 去重只针对字符串,不针对任意对象。
freeze
未执行重复数据删除。- 重复数据删除仅由
String#-@
从 Ruby 2.5 开始执行。
那篇文章还有第四个说法是错误的,尽管我们不能真的为此责怪作者,因为这篇文章是 2016 年的,而决定是在 2019 年才改变的:Ruby 3.0 will not have immutable string literals by default。
在那篇文章中 是 正确的一件事是 # frozen_string_literal: true
pragma(或相应的命令行选项 --enable-frozen-string-literal
)不仅会冻结所有静态字符串文字,它也会删除它们。