评估冻结的字符串
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
- 这个 属性(即只计算一次)叫什么?它应该与不变性不同。
- 我对字符串出现这种属性的情况的理解是否正确?官方文档在哪里提到这个?
- 有没有办法让内插字符串只计算一次?
- Interning。这些字符串据说是 interned.
不完全。更像是解释器是否可以在评估字符串之前决定字符串的值是什么。例如,考虑:
5.times { puts "#{'foo'}".object_id }
虽然有插值,但id是一样的
- 没有。这是一个内部优化。
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
我的模糊理解是,使用 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
- 这个 属性(即只计算一次)叫什么?它应该与不变性不同。
- 我对字符串出现这种属性的情况的理解是否正确?官方文档在哪里提到这个?
- 有没有办法让内插字符串只计算一次?
- Interning。这些字符串据说是 interned.
不完全。更像是解释器是否可以在评估字符串之前决定字符串的值是什么。例如,考虑:
5.times { puts "#{'foo'}".object_id }
虽然有插值,但id是一样的
- 没有。这是一个内部优化。
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