为什么符号不是冻结的字符串?

时间:2012-06-18 15:01:39

标签: ruby symbols

我理解字符串和符号之间的理论差异。我理解符号用于表示概念或名称或标识符或标签或键,而字符串是一个字符包。我知道字符串是可变的和瞬态的,其中符号是不可变的和永久的。我甚至喜欢在我的文本编辑器中符号看起来与Strings不同。

让我感到困扰的是,实际上,符号与字符串非常相似,因为它们不是实现这样的事实会引起很多麻烦。他们甚至不支持鸭子打字或暗示强制,不像其他着名的“相同但不同”的夫妻,Float和Fixnum。

当然,最大的问题是从其他地方进入Ruby的哈希,比如JSON和HTTP CGI,使用字符串键而不是符号键,因此Ruby程序必须向后弯曲才能在前面或在查找时间。 HashWithIndifferentAccess的存在,以及它在Rails和其他框架中的猖獗使用,表明这里存在一个问题,需要抓一点痒。

有谁能告诉我为什么Symbols不应该被冻结的实际原因?除了“因为它总是如何完成”(历史)或“因为符号不是字符串”(乞求问题)。

考虑以下令人惊讶的行为:

:apple == "apple"  #=> false, should be true

:apple.hash == "apple".hash #=> false, should be true

{apples: 10}["apples"]  #=> nil, should be 10

{"apples" => 10}[:apples]  #=> nil, should be 10

:apple.object_id == "apple".object_id #=> false, but that's actually fine

所有这一切都需要让下一代Rubyists不那么困惑:

class Symbol < String
  def initialize *args
    super
    self.freeze
  end

(还有很多其他的库级黑客攻击,但仍然不太复杂)

另见:

更新:我认为Matz在class Symbol < String很好地说明了{{1}}:http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/9192(感谢Azolo挖掘它,以及Matz'最终撤回)。

6 个答案:

答案 0 :(得分:8)

这个答案与我的original answer完全不同,但我在Ruby邮件列表上遇到了一对interesting threads。 (两个好读)

因此,在2006年的某个时刻,matz将Symbol类实现为Symbol < String。然后剥离Symbol类以消除任何可变性。所以Symbol实际上是一个不可变的String

然而,它被还原了。 reason given was

  

即使它高度反对DuckTyping,人们也倾向于使用案例   在类和符号&lt;字符串经常会导致严重的问题。

所以问题的答案仍然是: Symbol就像String,但事实并非如此。
问题不在于Symbol不应该是String,而是在历史上不是。{/ p>

答案 1 :(得分:5)

我不知道一个完整的答案,但这里有很大一部分:

符号用于散列键的原因之一是给定符号的每个实例都是完全相同的对象。这意味着:apple.id将始终返回相同的值,即使您没有传递它。另一方面,"apple".id每次都会返回一个不同的id,因为会创建一个新的字符串对象。

这就是为什么符号被推荐用于散列键的原因。使用符号时,不需要进行对象等效性测试。它可以直接与对象身份短路。

答案 2 :(得分:1)

另一个考虑是"apple".each_char有意义,但:apple.each_char没有。字符串是“有序的字符列表”,但符号是没有显式值的原子数据点。

我会说HashWithIndifferentAccess实际上证明Ruby符号正在履行两个不同的角色; 符号(基本上就像其他语言中的枚举)和实习字符串(基本上是抢先优化,补偿了解释ruby的事实,因此没有智能优化编译器的好处。)

答案 3 :(得分:0)

请参阅此答案:https://stackoverflow.com/a/6745253/324978

主要原因:性能(符号存储为整数,永远不会被垃圾回收)和一致性(:admin:admin将始终指向同一个对象,其中"admin"和{ {1}}没有那个保证)等等。

答案 4 :(得分:0)

它的基础是,这些不应该是真的:

:apple == "apple"  #=> false, should be true

:apple.hash == "apple".hash #=> false, should be true

符号始终是相同的对象,文本不是。

答案 5 :(得分:0)

如果一个String可以继承Symbol,因为它增加了很多功能(变异)。但是Symbol永远不能用作“字符串”,因为在所有需要变异的情况下它都会失败。

在任何情况下,正如我上面所说,string == symbol必须永远不会返回true,如上所述。如果您对此有所了解,您会注意到在考虑子类实例的类中没有合理的==实现。