为什么Matz默认在Ruby中选择使Strings变为可变?

时间:2010-04-09 15:01:02

标签: ruby string language-design immutability mutable

与此问题相反:Why can't strings be mutable in Java and .NET?

这种选择是否仅仅因为操作(追加等)在可变字符串上有效,或者是否有其他原因而在Ruby中进行?

(如果只是效率,这看起来很奇怪,因为Ruby的设计似乎不会高度重视促进有效的实施。)

2 个答案:

答案 0 :(得分:32)

正如您所说,这与Ruby的设计一致。不可变字符串比可变字符串更有效 - 减少复制,因为字符串被重用 - 但使程序员更难工作。将字符串视为可变是直观的 - 您可以将它们连接在一起。为了解决这个问题,Java默默地将两个字符串的连接(通过+)转换为使用StringBuffer对象,我确信还有其他类似的黑客攻击。 Ruby选择默认情况下使字符串变为可变,但会牺牲性能。

Ruby还有许多破坏性方法,例如依赖于字符串可变的String#upcase!

另一个可能的原因是Ruby受到Perl的启发,而Perl碰巧使用了可变字符串。

Ruby有符号和冻结字符串,两者都是不可变的。作为额外的奖励,符号保证每个可能的字符串值是唯一的。

答案 1 :(得分:5)

这些是我的意见,而不是Matz的意见。出于这个答案的目的,当我说一个语言有“不可变字符串”时,这意味着 all 它的字符串是不可变的,即无法创建一个可变的字符串。

  1. “不可变字符串”设计将字符串视为标识符(例如,作为散列键和其他VM内部使用)和数据存储结构。这个想法是标识符可变是危险的。对我而言,这听起来像违反了单一责任。在Ruby中,我们有标识符的符号,因此字符串可以自由地充当数据存储。 Ruby确实允许字符串作为哈希键,但我认为程序员很少将字符串存储到变量中,将其用作哈希键,然后修改字符串。在程序员的脑海中,有(或应该)2个字符串的分离。通常,用作散列键的字符串是文字字符串,因此几乎没有机会进行变异。使用字符串作为散列键与使用两个字符串的数组作为散列键没有太大区别。只要你的头脑能够很好地掌握你所使用的钥匙,就没有问题。

  2. 从认知简单性的角度来看,将字符串作为数据存储是有用的。只考虑Java及其StringBuffer。这是一个额外的数据结构(在一个已经很大且通常不直观的标准库中),如果你正在尝试执行字符串操作,比如在另一个字符串的某个索引处插入一个字符串,则必须管理它。因此,一方面,Java认识到需要进行这些类型的操作,但是因为不可变的字符串暴露给程序员,所以它们必须引入另一个结构,因此操作仍然可行,而不会让我们重新发明轮子。这给程序员带来了额外的认知负担。

  3. 在Python中,最简单的插入方式似乎是在插入点之前和之后获取子串,然后将它们连接到要插入的字符串周围。我想他们可以轻松地向插入并返回新字符串的标准库添加一个方法。但是,如果该方法被称为insert,初学者可能会认为它会改变字符串;为了描述它必须被称为new_with_inserted或类似的东西。在日常使用中,“插入”意味着您更改插入的内容的内容(例如,将信封插入邮箱会更改邮箱的内容)。同样,这引出了一个问题,“为什么我不能改变我的数据存储?”

  4. Ruby提供冻结对象,因此可以安全地传递它们而不会引入微妙的错误。好处是Ruby像处理任何其他数据结构(数组,哈希,类实例)一样处理字符串;他们都可以被冻结。一致性是程序员友好的。不可变的字符串使字符串成为一种“特殊的”数据结构,如果你将其用作数据存储,则不是真的。