Почему Мац решил сделать строки изменяемыми по умолчанию в Ruby?

Это обратная сторона этого вопроса: Почему строки не могут быть изменяемыми в Java и .NET?

Был ли этот выбор сделан в Ruby только потому, что операции (добавления и тому подобное) эффективны для изменяемых строк, или была какая-то другая причина?

(Если речь идет только об эффективности, это может показаться странным, поскольку в остальном дизайн Ruby, по-видимому, не придает большого значения облегчению эффективной реализации.)


person Seth Tisue    schedule 09.04.2010    source источник
comment
Примечание. Я очень мало знаю о Ruby. Но меня интересует языковой дизайн.   -  person Seth Tisue    schedule 09.04.2010
comment
Почему бы тебе не спросить Маца? Вероятно, у него есть лучший ответ, чем у нас.   -  person Sam Post    schedule 09.04.2010
comment
@Sam: ха-ха, хорошая мысль. Я надеюсь, что он где-то писал об этом, и кто-нибудь мог бы указать мне на это или резюмировать.   -  person Seth Tisue    schedule 09.04.2010


Ответы (2)


Как вы заметили, это соответствует дизайну Ruby. Неизменяемые строки более эффективны, чем изменяемые строки — меньше копирования, так как строки используются повторно — но усложняют работу программисту. Интуитивно рассматривать строки как изменяемые — вы можете объединять их вместе. Чтобы справиться с этим, Java незаметно переводит конкатенацию (через +) двух строк в использование объекта StringBuffer, и я уверен, что есть и другие подобные приемы. Вместо этого Ruby предпочитает делать строки изменяемыми по умолчанию за счет производительности.

Ruby также имеет ряд деструктивных методов, таких как String#upcase!, которые полагаются на изменяемость строк.

Другая возможная причина заключается в том, что Ruby вдохновлен Perl, а Perl использует изменяемые строки.

Ruby имеет символы и замороженные строки, оба неизменяемы. В качестве дополнительного бонуса символы гарантированно уникальны для каждого возможного строкового значения.

person rjh    schedule 09.04.2010
comment
В ПОРЯДКЕ. Но почему строки по умолчанию изменяемы? Я отредактировал вопрос и добавил по умолчанию. - person Seth Tisue; 09.04.2010
comment
Строку можно сделать неизменяемой, вызвав для нее .frozen, но на самом деле вы не можете сделать неизменяемую строку изменяемой — это нарушит принцип. Например, если я передам неизменяемую строку функции, я не ожидаю, что функция сделает ее изменяемой и начнет ее изменять. - person rjh; 09.04.2010
comment
Это также может быть связано с тем, что рубиновая строка считается перечислимой. Строка рассматривается как список символов, естественное ожидание состоит в том, что вы можете добавлять и удалять из списка. Реализация неизменяемых строк по умолчанию и по-прежнему применение перечисляемой семантики было бы болезненным. - person Jean; 09.04.2010
comment
@Jean, строка не перечислима в ›= 1.9 - person horseyguy; 09.04.2010
comment
@banister: он по-прежнему изменяем по умолчанию? (Я еще не играл с ruby ​​›1.8, но я удивлен, что это больше не перечисляемое, это довольно большое изменение API, не так ли? Или просто он больше не включает Enumerable, но все еще responses_to?большая часть апи?) - person Jean; 09.04.2010
comment
@Jean, он все еще изменяем, просто не включает Enumerable. Основная причина невозможности перечисления состоит в том, что невозможно определить естественную единицу. Итерация по строкам или символам? Например. - person horseyguy; 09.04.2010
comment
* должно быть .freeze, а не .frozen :S - person rjh; 09.04.2010
comment
@banister: также поддержка собственной кодировки строк. Вы перечисляете байты или символы? Вот почему у нас есть String#each_byte и String#each_char. - person rjh; 09.04.2010
comment
@Jean: String не является Enumerable, потому что он рассматривается как список символов. Если бы это было так, это было бы Enumerable по символу, а не по строке, как в 1.8. - person sepp2k; 09.04.2010
comment
Я очарован идеей, что неизменяемость создает дополнительную работу для меня как программиста. Мое представление о дополнительной работе заключается в том, что я должен быть очень осторожным с тем, кому я показываю строку, потому что кто-нибудь может ее видоизменить! Мне нравятся мои неизменяемые строки! - person Norman Ramsey; 10.04.2010
comment
@ sepp2k на самом деле это и то, и другое (each_byte), но я думаю, что каждая строка по умолчанию имела больше смысла в то время ... @Norman, может быть, я был просто травмирован некоторыми использованиями StringBuilder ... - person Jean; 10.04.2010
comment
@Jean: Да, у вас есть each_byte, но все методы Enumerable (map, select, inject) работают со строками, потому что каждый из них работает со строками. Поэтому говорить, что это Enumerable по символам, потому что у него есть each_byte (который на самом деле по байтам, а не по символам, но это касается точки), я думаю, немного неточно, поскольку each_byte не имеет ничего общего с Enumerable. - person sepp2k; 10.04.2010
comment
@Norman: тогда вам следует очень беспокоиться о передаче хэшей, массивов или объектов другим функциям :) - person rjh; 10.04.2010
comment
@rjh: в эти дни мой психиатр не разрешает мне ничего, кроме целых чисел и чисел с плавающей запятой --- и она не так уверена в числах с плавающей запятой. - person Norman Ramsey; 10.04.2010
comment
Как именно неизменяемые строки усложняют работу программиста? - person cletus; 18.04.2010
comment
В чем неизменяемые строки более эффективны, чем изменяемые? Мне нравятся неизменяемые строки так же, как и следующему парню, но, безусловно, есть сценарии, когда изменяемая строка работает быстрее. Особенно, если количество байтов на символ фиксировано, чего, как мне кажется, в Ruby нет. - person Jonas Elfström; 09.01.2012
comment
Может быть, он имеет в виду более эффективный с точки зрения памяти? Установите 5 переменных в одну и ту же неизменяемую строку (по крайней мере, в Python и Java), и у вас будет только 1 строковый объект (и 5 ссылок) в памяти. - person Matt Luongo; 19.10.2012
comment
На самом деле, когда дело доходит до внутренней работы оператора + в Java, он не обязательно должен быть StringBuffer, можно использовать и StringBuilder. В спецификации упоминается StringBuffer или аналогичный метод - person toniedzwiedz; 08.10.2014
comment
Как бы то ни было, это поведение, возможно, изменится в Ruby 3.0. - person shevy; 14.09.2015

Это мое мнение, а не Маца. Для целей этого ответа, когда я говорю, что язык имеет «неизменяемые строки», это означает, что все его строки являются неизменяемыми, т. е. невозможно создать изменяемую строку.

  1. Дизайн «неизменяемой строки» рассматривает строки как как идентификаторы (например, в качестве хэш-ключей и других внутренних применений виртуальной машины), так и как структуры хранения данных. Идея состоит в том, что для идентификаторов опасно быть изменчивыми. Для меня это звучит как нарушение единоличной ответственности. В Ruby у нас есть символ для идентификаторов, поэтому строки могут действовать как хранилища данных. Это правда, что Ruby позволяет использовать строки в качестве хеш-ключей, но я думаю, что программист редко сохраняет строку в переменной, использует ее как хэш-ключ, а затем модифицирует строку. По мнению программиста, существует (или должно быть) разделение на 2 использования строк. Часто строка, используемая в качестве хеш-ключа, является буквальной строкой, поэтому маловероятно, что она будет видоизменена. Использование строки в качестве хеш-ключа мало чем отличается от использования массива из двух строк в качестве хеш-ключа. Пока ваш разум хорошо понимает, что вы используете в качестве ключа, проблем нет.

  2. Наличие строки в качестве хранилища данных полезно с точки зрения простоты познания. Просто подумайте о Java и ее StringBuffer. Это дополнительная структура данных (в уже большой и часто неинтуитивной стандартной библиотеке), которой вам приходится управлять, если вы пытаетесь выполнять строковые операции, такие как вставка одной строки по определенному индексу другой строки. Итак, с одной стороны, Java признает необходимость выполнения таких операций, но, поскольку неизменяемые строки доступны программисту, им пришлось ввести другую структуру, чтобы операции по-прежнему были возможны, не заставляя нас изобретать велосипед. Это создает дополнительную когнитивную нагрузку на программиста.

  3. В Python кажется, что самый простой способ вставки — это захватить подстроки до и после точки вставки, а затем объединить их вокруг вставляемой строки. Я предполагаю, что они могли бы легко добавить в стандартную библиотеку метод, который вставляет и возвращает новую строку. Однако, если метод называется insert, новички могут подумать, что он изменяет строку; чтобы быть описательным, его нужно было бы назвать new_with_inserted или что-то в этом роде. В повседневном использовании «вставка» означает, что вы изменяете содержимое вставленных вещей (например, вставка конверта в почтовый ящик изменяет содержимое почтового ящика). Опять же, возникает вопрос: «Почему я не могу изменить свое хранилище данных?»

  4. Ruby обеспечивает замораживание объектов, поэтому их можно безопасно передавать без внесения незаметных ошибок. Приятно то, что Ruby обрабатывает строки так же, как и любую другую структуру данных (массивы, хэши, экземпляры классов); все они могут быть заморожены. Согласованность удобна для программиста. Неизменяемые строки выделяют строки как «особую» структуру данных, хотя на самом деле это не так, если вы используете их в качестве хранилища данных.

person Kelvin    schedule 09.05.2013