29

我了解字符串和符号之间的理论区别。我知道符号是用来表示一个概念或一个名称或一个标识符或一个标签或一个键,而字符串是一袋字符。我知道字符串是可变的和瞬态的,而符号是不可变的和永久的。我什至喜欢在我的文本编辑器中符号与字符串的不同之处

困扰我的是,实际上,符号与字符串非常相似,以至于它们没有作为字符串实现的事实引起了很多头痛。它们甚至不支持鸭式或隐式强制转换,这与另一对著名的“相同但不同”的组合 Float 和 Fixnum 不同。

当然,最大的问题是从其他地方进入 Ruby 的散列,比如 JSON 和 HTTP CGI,使用字符串键,而不是符号键,所以 Ruby 程序必须向后弯腰,要么在前面转换它们,要么在查找​​时转换它们。的存在HashWithIndifferentAccess,以及它在 Rails 和其他框架中的猖獗使用,表明这里有一个问题,一个需要解决的问题。

谁能告诉我为什么符号不应该被冻结字符串的实际原因?除了“因为它总是这样做”(历史)或“因为符号不是字符串”(乞求问题)之外。

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

: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

让下一代 Ruby 主义者不那么困惑的所有事情就是:

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

(以及许多其他库级别的黑客攻击,但仍然不太复杂)

也可以看看:

更新:我认为 Matz 在class Symbol < String这里很好地说明了这一点:http: //blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/9192(感谢 Azolo 挖掘了这个,并且Matz 也最终撤回)。

4

6 回答 6

9

这个答案与我原来的答案截然不同,但我在 Ruby 邮件列表中遇到了几个有趣的 线程。(两本好书)

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

然而,它被恢复了。给出的理由是

尽管它非常反对 DuckTyping,但人们倾向于在类上使用 case,而 Symbol < String 经常会导致严重的问题。

所以你的问题的答案仍然是:aSymbol就像 a String,但它不是。
问题不是 a Symbolshould not be String,而是历史上不是。

于 2012-06-18T15:52:02.007 回答
5

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

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

这种差异就是为什么建议使用符号作为散列键的原因。使用符号时无需进行对象等效性测试。它可以直接短路到对象标识。

于 2012-06-18T15:09:02.767 回答
1

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

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

于 2012-06-19T23:47:06.750 回答
0

看到这个答案:https ://stackoverflow.com/a/6745253/324978

主要原因:性能(符号存储为整数,并且永远不会被垃圾收集)和一致性(:admin并且:admin将始终指向同一个对象,在哪里"admin""admin"没有那个保证)等。

于 2012-06-18T15:12:20.023 回答
0

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

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

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

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

于 2012-06-18T16:05:57.673 回答
0

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

无论如何,正如我上面所说,字符串 == 符号绝不能像上面建议的那样返回 true。如果您对此稍加考虑,您会注意到在考虑子类实例的类中没有合理的 == 实现。

于 2012-06-20T08:46:18.153 回答