нетехнические преимущества неизменяемости строкового типа

Мне интересно узнать о преимуществах неизменности строкового типа с точки зрения программистов.

Технические Преимущества (на стороне компилятора / языка) сводятся в основном к тому, что оптимизировать легче, если тип неизменен. Прочитайте здесь для соответствующего вопроса.

Кроме того, в непостоянном строковом типе либо у вас уже встроена безопасность потоков (опять же, оптимизация труднее сделать), либо вы должны это сделать сами. В любом случае у вас будет выбор использовать изменяемый строковый тип со встроенной безопасностью потоков, так что это не является преимуществом неизменяемых строковых типов. (Опять же, будет проще выполнять обработку и оптимизацию, чтобы обеспечить безопасность потоков для неизменяемого типа, но это не главное.)

Но каковы преимущества использования неизменяемых строковых типов? Какой смысл иметь неизменяемые типы, а другие нет? Это кажется мне очень противоречивым.

В C ++, если я хочу, чтобы какая-то строка была неизменной, я передаю ее как константную ссылку на функцию ( const std :: string & ). Если я хочу получить изменяемую копию исходной строки, я передаю ее как std :: string . Только если я хочу, чтобы он был изменяемым, я передаю его как ссылку ( std :: string & ). Так что у меня просто есть выбор, что я хочу сделать. Я могу просто сделать это с каждым возможным типом.

В Python или в Java некоторые типы являются неизменяемыми (в основном все примитивные типы и строки), другие - нет.

В чистых функциональных языках, таких как Haskell, все является неизменным. 12238 Есть ли веская причина, почему имеет смысл иметь это несоответствие? Или это просто по техническим причинам низкого уровня?

17
задан Community 23 May 2017 в 12:16
поделиться

8 ответов

Какой смысл иметь типы неизменяемые, а другие нет?

Без некоторых изменяемых типов вам пришлось бы изо всех сил переходить к чистому функциональному программированию — совершенно другой парадигме, чем ООП и процедурные подходы, которые в настоящее время наиболее популярны. , и, хотя он чрезвычайно мощный, очевидно, очень сложный для многих программистов (что происходит, когда вам нужны побочные эффекты на языке, где ничего нельзя изменить, а в реальном программировании вы, конечно, неизбежно делаете, является частью проблемы - Монады в Haskell представляют собой, например, очень элегантный подход, но сколько вы знаете программистов, которые полностью и уверенно понимают их и могут использовать их, а также типичные конструкции ООП?-) .

Если вы не понимаете огромную ценность наличия множества доступных парадигм (как FP , так и принципиально основанных на изменчивых данных), я рекомендую изучить шедевр Хариди и Ван Роя, Concepts, Методы и модели компьютерного программирования -- "SICP для 21-го века", как я однажды описал это ;-).

Большинство программистов, знакомых с Хариди и Ван Роем или нет, с готовностью признают, что для них важно иметь по крайней мере некоторые изменяемые типы данных. Несмотря на фразу, которую я процитировал выше из вашего Вопроса, которая придерживается совершенно иной точки зрения, я полагаю, что это также может быть корнем вашего недоумения: не «почему некоторые из каждого», а скорее «почему некоторые неизменяемые вообще".

«Полностью изменяемый» подход однажды (случайно) был получен в реализации на Фортране. Если бы у вас было, скажем,

  SUBROUTINE ZAP(I)
  I = 0
  RETURN

то фрагмент программы, выполняющий, например,

  PRINT 23
  ZAP(23)
  PRINT 23

вывел бы 23, затем 0 -- число 23 было видоизменено, так что все ссылки на 23 в остальной части на самом деле программа будет ссылаться на 0. Технически это не ошибка компилятора: в Фортране были тонкие правила о том, что ваша программа и что не разрешено делать при передаче констант и переменных процедурам, которые присваиваются их аргументам, и этот фрагмент нарушает эти малоизвестные, не применимые к компилятору правила, так что это но в программе, а не в компиляторе.На практике, конечно, количество ошибок, вызванных таким образом, было неприемлемо велико, поэтому типичные компиляторы вскоре переключились на менее разрушительное поведение в таких ситуациях (помещение констант в сегменты только для чтения, чтобы получить ошибку времени выполнения, если ОС это поддерживала; или , передавая свежую копию константы, а не саму константу, несмотря на накладные расходы и т. д.), хотя технически это были программные ошибки, позволяющие компилятору отображать неопределенное поведение вполне "правильно" ;-).

Альтернативой, применяемой в некоторых других языках, является добавление сложности нескольких способов передачи параметров - особенно, возможно, в C++, что с использованием по значению, по ссылке, по постоянной ссылке, по указателю, по постоянному указателю, ... и тогда, конечно, вы видите программистов, сбитых с толку такими объявлениями, как const foo* const bar (где самый правый const в основном не имеет значения, если bar является аргумент какой-либо функции... но решающий вместо этого, если bar является локальной переменной...!-).

На самом деле Алгол-68, вероятно, пошел еще дальше в этом направлении (если у вас может быть значение и ссылка, почему бы не ссылка на ссылку? или ссылка на ссылку на ссылку? а правила, определяющие, что происходит, представляют собой, пожалуй, самую тонкую и сложную смесь, когда-либо найденную в языке программирования, «предназначенном для реального использования»).Ранний C (который имел только by-value и by-explicit-pointer — без const, без ссылок, без усложнений), без сомнения, отчасти был реакцией на него, как и исходный Pascal. Но вскоре вкралась const, и сложности снова начали расти.

Java и Python (наряду с другими языками) прорезают эту дебрь с помощью мощного мачете простоты: вся передача аргументов, и все присваивания осуществляются "по ссылке на объект" (никогда не ссылайтесь на переменную или другую переменную). ссылка, а не семантически неявные копии и т. д.). Определение (по крайней мере) чисел как семантически неизменяемых сохраняет здравомыслие программистов (а также этот драгоценный аспект простоты языка), избегая «упс», подобных тому, что демонстрирует приведенный выше код Fortran.

Обращение со строками как с примитивами точно так же, как и с числами, вполне согласуется с предполагаемым высоким семантическим уровнем языков, потому что в реальной жизни нам действительно нужны строки, которые так же просты в использовании, как и числа; альтернативы, такие как определение строк в виде списков символов (Haskell) или массивов символов (C), создают проблемы как для компилятора (сохраняя эффективную производительность при такой семантике), так и для программиста (фактически игнорируя эту произвольную структуру, позволяющую использовать строки как простые). примитивы, как часто требуется при программировании в реальной жизни).

Python пошел еще дальше, добавив простой неизменяемый контейнер (tuple) и привязав хэширование к «эффективной неизменности» (что позволяет избежать определенных сюрпризов для программиста, которые могут быть найдены, например , в Perl, с его хэшами, позволяющими использовать изменяемые строки в качестве ключей) — а почему бы и нет? Если у вас есть неизменяемость (драгоценная концепция, которая избавляет программиста от необходимости изучать N различных семантик для присваивания и передачи аргументов, где N имеет тенденцию к увеличению со временем ;-), вы также можете извлечь из этого полную выгоду ;-) .

16
ответ дан 30 November 2019 в 13:20
поделиться

Если вам нужна полная согласованность, вы можете сделать только все неизменяемыми, потому что изменяемые Bools или Ints просто не имеют никакого смысла. Некоторые функциональные языки так и делают.

Философия Python гласит: «Просто лучше, чем сложно». В C вам нужно знать, что строки могут меняться, и думать о том, как это может повлиять на вас. Python предполагает, что вариант использования строк по умолчанию — это «собрать текст вместе» — вам абсолютно ничего не нужно знать о строках, чтобы сделать это. Но если вы хотите изменить свои строки, вам просто нужно использовать более подходящий тип (например, списки, StringIO, шаблоны и т. д.).

1
ответ дан 30 November 2019 в 13:20
поделиться

Не уверен, что вы считаете это преимуществом «технического низкого уровня», но тот факт, что неизменяемая строка неявно является потокобезопасной, позволяет сэкономить много усилий при написании кода для обеспечения безопасности потоков.

Слегка игрушечный пример...

Тема A – Проверить, есть ли у пользователя с логином FOO разрешение на выполнение каких-либо действий, вернуть true

Тема B – Изменить строку пользователя на имя для входа BAR

Тема A – Выполнить некоторые операция с именем пользователя BAR из-за предыдущей проверки разрешений, пройденной против FOO.

Тот факт, что строка не может измениться, избавляет вас от необходимости защищаться от этого.

1
ответ дан 30 November 2019 в 13:20
поделиться

Если строки изменяемы, то многим потребителям строки придется делать ее копии. Если строки являются неизменяемыми, это гораздо менее важно (если неизменяемость обеспечивается аппаратными блокировками, для некоторых потребителей строки, заботящихся о безопасности, может быть неплохой идеей создавать свои собственные копии на случай, если предоставленные им строки неверны). t настолько неизменны, насколько они должны быть).

Класс StringBuilder довольно хорош, хотя я думаю, что было бы лучше, если бы он имел свойство «Value» (чтение было бы эквивалентно ToString, но оно отображалось бы в инспекторах объектов; запись позволяла бы напрямую устанавливать все значения). content) и расширяющее преобразование по умолчанию в строку. Теоретически было бы неплохо иметь тип MutableString, унаследованный от общего предка со String, чтобы изменяемая строка могла быть передана функции, которой все равно, является ли строка изменяемой, хотя я подозреваю, что оптимизации, основанные на этом факте, то, что строки имеют определенную фиксированную реализацию, было бы менее эффективным.

1
ответ дан 30 November 2019 в 13:20
поделиться

В языке со ссылочной семантикой для определяемых пользователем типов наличие изменяемых строк было бы катастрофой, потому что каждый раз, когда вы назначаете строковую переменную, вы создаете псевдоним изменяемого строкового объекта, и вы приходится везде делать защитные копии. Вот почему строки неизменяемы в Java и C# — если строковый объект неизменен, не имеет значения, сколько переменных на него указывает.

Обратите внимание, что в C++ две строковые переменные никогда не имеют общего состояния (по крайней мере, концептуально — технически может происходить копирование при записи, но это выходит из моды из-за неэффективности в многопоточные сценарии).

1
ответ дан 30 November 2019 в 13:20
поделиться

Тем не менее я не уверен, можно ли считать это нетехническим: если строки изменяемы, то большинству(*) коллекций нужно для создания закрытых копий своих строковых ключей.

В противном случае внешнее изменение ключа «foo» на «bar» приведет к тому, что «bar» окажется во внутренних структурах коллекции, где ожидается «foo». Таким образом, поиск «foo» найдет «bar», что является меньшей проблемой (ничего не вернуть, переиндексировать оскорбительный ключ), но поиск «bar» ничего не найдет, что является большей проблемой.

(*) Простой набор, выполняющий линейный просмотр всех ключей при каждом поиске, не должен этого делать, поскольку он естественным образом приспосабливается к изменениям ключей.

2
ответ дан 30 November 2019 в 13:20
поделиться

Нет всеобъемлющей, фундаментальной причины не иметь изменяемых строк. Лучшее объяснение их неизменности, которое я нашел, состоит в том, что это способствует более функциональному способу программирования с меньшими побочными эффектами. В итоге получается чище, элегантнее и более Pythonic.

Семантически они должны быть неизменными, не так ли? Строка "hello" всегда должна представлять "hello". Вы не можете изменить его больше, чем вы можете изменить число три!

1
ответ дан 30 November 2019 в 13:20
поделиться

Основное преимущество для программиста заключается в том, что с изменяемыми строками вам не нужно беспокоиться о том, кто может изменить вашу строку. Таким образом, вам никогда не придется сознательно решать: «Должен ли я скопировать эту строку сюда?».

1
ответ дан 30 November 2019 в 13:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: