评估冻结的字符串

Evaluating a frozen string

我的模糊理解是,使用 Ruby 2.2 的 frozen 字符串方法或 Ruby 2.3 的 frozen-string-literal: true pragma,相关的冻结字符串文字是在整个程序执行过程中仅计算一次 当且仅当字符串没有插值时 。以下似乎说明了这一点:

未插值

#frozen-string-literal: true
5.times{p "".object_id}

输出(相同的对象 ID):

70108065381260
70108065381260
70108065381260
70108065381260
70108065381260

插值

#frozen-string-literal: true
5.times{p "#{}".object_id}

输出(不同的对象 ID):

70108066220720
70108066220600
70108066220420
70108066220300
70108066220180
  1. 这个 属性(即只计算一次)叫什么?它应该与不变性不同。
  2. 我对字符串出现这种属性的情况的理解是否正确?官方文档在哪里提到这个?
  3. 有没有办法让内插字符串只计算一次?
  1. Interning。这些字符串据说是 interned.
  2. 不完全。更像是解释器是否可以在评估字符串之前决定字符串的值是什么。例如,考虑:

    5.times { puts "#{'foo'}".object_id }
    

    虽然有插值,但id是一样的

  3. 没有。这是一个内部优化。 Object#freeze 的要点是不变性。


更新:只有文字字符串被内部化。这是显而易见的 here.

我找不到负责插值的代码部分。所以我不确定为什么 "#{'foo'}" 被认为是文字字符串。请注意,无论这种翻译发生在哪里,它都处于较低的解析器级别,并且发生在任何实际处理之前。 String#freeze 映射到 rb_str_freeze, which doesn't call opt_str_freeze.

的事实证明了这一点

"Frozen" 与字符串是否被评估不止一次无关。这是,你是对的,关于可变性。

每次遇到包含它的行时,都会评估 字符串文字。

让它只被评估一次的(唯一)方法是将它放在一行只执行一次的源代码中,而不是放在循环中。每次在程序流中执行该行源代码时,总是会评估循环(或源代码的任何其他部分)中的字符串文字。

这确实是一个单独的事情,而不是它是否是frozen/immutable,一旦评估。

接受的答案有点误导。 "It is more like if the interpreter can decide what the value of the string would be before evaluating it." 没有。一点也不。它需要被评估。如果字符串被冻结,那么一旦它被评估,它将使用内存中的相同位置和相同的 object/object_id (这是表达同一事物的两种方式)作为所有其他等效字符串。但它仍在评估中,有或没有插值。

(如果没有插值,字符串文字的 'evaluation' 非常非常快。使用简单的插值通常也非常快。您当然可以使用插值来调用昂贵的方法,假设)。

如果没有插值,我根本不会担心。对于插值,如果您认为您的插值足够昂贵,您不想在循环中进行——避免它的唯一方法是不要在循环中进行,而是在循环外创建一次字符串。

Ruby 文档可能谈论 "String literals" 而不是 "literal Strings"。 "String literal" 是由源代码中的字节创建的任何字符串(使用 ''""%Q[] 或在 ruby).有或没有插值。

那么,什么样的字符串不是由字符串文字创建的?好吧,例如,通过从文件或网络中读取字节创建的字符串。或者通过获取现有字符串并在其上调用方法创建的字符串 returns 一个副本,如 some_string.dup。 "String literal" 表示在源代码中逐字创建的字符串,而不是通过从外部输入读取。 http://ruby-doc.org/core-2.1.1/doc/syntax/literals_rdoc.html