我的模糊理解是,使用Ruby 2.2的frozen
方法对字符串或Ruby 2.3的frozen-string-literal: true
编译指示,相关的冻结字符串文字仅在整个过程中被评估一次程序执行当且仅当字符串没有插值时。以下似乎说明了这一点:
未插补
#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
答案 0 :(得分:7)
不完全。更像是解释器在评估之前可以决定字符串的值。例如,考虑:
5.times { puts "#{'foo'}".object_id }
即使涉及插值,id也是相同的。
Object#freeze
的要点是不变性。<小时/> 更新:只有文字字符串被内化。这很明显here。
我无法找到负责插值的代码部分。所以我不确定为什么"#{'foo'}"
被视为文字字符串。请注意,无论何处发生此转换,它都处于较低的解析器级别,并且在任何实际处理之前发生。 String#freeze
映射到rb_str_freeze
并且不会调用opt_str_freeze
这一事实就是明证。
答案 1 :(得分:-1)
“冻结”不是关于字符串是否被多次评估。关于可变性,你是对的。
每次遇到包含它的行时,字符串文字将被评估。
只对它进行一次评估的(唯一)方法是将它放在一行只执行一次的源代码中,而不是在循环中。每次在程序流中执行该行源代码时,将始终评估循环(或源代码的任何其他部分)中的字符串文字。
一旦评估,这确实是一个单独的事情,而不是冻结/不可变。
接受的答案有点误导。 “更像是解释器在评估之前可以决定字符串的值。”不。一点也不。需要对其进行评估。如果字符串被冻结,那么一旦它被评估,它将在内存中使用相同的位置,并使用与所有其他等效字符串相同的object / object_id(这两种方式相同)。但无论是否插值,它仍在评估中。
(没有插值,字符串文字的'评价'非常非常快。通过简单的插值,它通常也很快。当然,您可以使用插值来调用昂贵的方法,但假设)。
没有插值,我根本不担心。使用插值,如果你认为你的插值足够昂贵你不想在循环中这样做 - 避免它的唯一方法是不在循环中执行它,而是在循环外创建一次字符串。
Ruby docs可能会谈论“String literals”而不是“literal Strings”。 “字符串文字”是由源代码中的字节创建的任何字符串(使用''
,""
,%Q[]
,或者在ruby中的源代码中创建字符串文字的任何其他方法) 。有或没有插值。
那么字符串文字创建的是什么类型的 ?好吧,例如,通过从文件或网络中读取字节来创建的字符串。或者通过获取现有字符串并在其上调用返回副本的方法(如some_string.dup
)创建的字符串。 “字符串文字”表示在源代码中按字面创建的字符串,而不是通过从外部输入读取。 http://ruby-doc.org/core-2.1.1/doc/syntax/literals_rdoc.html