【问题标题】:Freezing string literals in early Ruby versions在早期 Ruby 版本中冻结字符串文字
【发布时间】:2019-09-18 08:19:31
【问题描述】:

这个问题特别适用于 Ruby 1.9 和 2.1,其中字符串文字不能自动冻结。特别是我指的是this article,它建议冻结字符串,以便重复评估代码不会每次都创建一个新的字符串对象,据说还有其他优点可以使程序性能更好。作为一个具体的例子,本文提出了表达方式

("%09d".freeze % id).scan(/\d{3}/).join("/".freeze)

我想在我们的项目中使用这个概念,出于测试目的,我尝试了以下代码:

3.times { x="abc".freeze; puts x.object_id }

在 Ruby 2.3 中,这每次都打印相同的对象 ID。在语言级别与 Ruby 1.9 对应的 JRuby 1.7 中,它会打印三个不同的对象 ID,尽管我已经明确地冻结了字符串。

有人能解释一下原因吗,在这种情况下如何正确使用freeze

【问题讨论】:

  • 字符串实习使字符串不可变,但使字符串不可变并不能保证字符串实习。 freeze 合约仅指定它将使字符串不可变,不多也不少。字符串是否被interned没有定义,每个实现都可以自己选择行为。
  • @JörgWMittag:怎么会这样?在unfreeze,您只需将其复制到其他地方(这样对“规范”冰柱的任何引用都将保持有效)。这显然改变了object_id,但是......我认为Hash已经做了类似的事情......a = "foo"; b = { a => 1 }; a.object_id == b.keys.first.object_id是错误的
  • 大声笑,你是对的(除了shenanigans)。

标签: ruby jruby


【解决方案1】:

我特别参考了这篇文章,它建议冻结字符串,这样重复评估代码不会每次都创建一个新的字符串对象

这不是Object#freeze 所做的。顾名思义,它“冻结”对象,即它不允许对对象的内部状态进行任何进一步的修改。文档中没有任何内容表明 Object#freeze 执行某种重复数据删除或实习。

可能想到String#-@,但这在Ruby 2.1 中不存在。它只是在 Ruby 2.3 中添加的,当时实际上有不同的语义:

  • Ruby 2.3–2.4:如果self 已经冻结,则返回self,否则返回self.dup.freeze,即a frozen duplicate of the string

    -strstr(冻结)

    如果字符串被冻结,则返回字符串本身。
    如果字符串没有被冻结,则复制字符串将其冻结并返回。

  • Ruby 2.5+:如果self 已经冻结,则返回self,否则返回字符串that is de-duplicated 的冻结版本(即,可以在现有冻结字符串的缓存中查找它并返回现有版本) :

    -strstr(冻结)

    返回一个冻结的,可能是预先存在的字符串副本。
    只要字符串没有被污染,或者上面设置了任何实例变量,就会对字符串进行重复数据删除。

因此,您链接到的文章在三个方面是错误的:

  1. 仅对字符串执行重复数据删除,而不是任意对象。
  2. freeze 不执行重复数据删除。
  3. 从 Ruby 2.5 开始,重复数据删除仅由 String#-@ 执行。

那篇文章中还有第四个说法是错误的,尽管我们不能真的责怪作者,因为这篇文章是 2016 年的,而且决定在 2019 年才改变:Ruby 3.0 will not have immutable string literals by default

在那篇文章中正确的一件事是# frozen_string_literal: true pragma(或相应的命令行选项--enable-frozen-string-literal)不仅会冻结所有静态字符串文字,它还会de - 复制它们。

【讨论】:

    猜你喜欢
    • 2013-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-01
    • 2010-10-22
    • 1970-01-01
    相关资源
    最近更新 更多