Я хотел бы узнать, как создавать графики и выполнять некоторые локальные операции над ними в Haskell, но этот вопрос не является специфическим для Haskell, и вместо графиков мы можем рассмотреть двусвязные списки.
Вопрос: Каким был бы идиоматический или рекомендуемый способ реализации двусвязного списка (или другой двусвязной или круговой структуры данных) и операций с ним на языке, который в основном поддерживает и поддерживает неизменные структуры данных (Haskell, Clojure и т. Д.) ? В частности, как использовать обновления на месте, которые формально запрещены языком?
Я легко могу представить, что если какая-то локальная операция выполняется над двусвязным списком (например, если элемент вставлен), может не потребоваться копировать весь список сразу из-за лени языка. Однако, поскольку список является двусвязным, если его изменить в одном месте, ни один из старых узлов не может быть использован в новой версии списка, и их рано или поздно придется каким-то образом пометить, скопировать, собрать мусор. , Очевидно, что это избыточные операции, если будет использоваться только обновленная копия списка, но они добавят «накладные расходы», пропорциональные размеру списка.
Означает ли это, что для таких задач неизменяемые данные просто неуместны, а функциональные декларативные языки без «нативной» поддержки изменяемых данных не так хороши, как императивные? Или есть какой-то хитрый обходной путь?
PS Я нашел несколько статей и презентаций на эту тему в Интернете, но с трудом следил за ними, хотя я думаю, что ответ на этот вопрос не должен занимать более одного абзаца и, возможно, диаграммы ... Я имею в виду, если есть нет «функционального» решения этой проблемы, ответ, вероятно, «использовать C». Если есть, то насколько это может быть сложно?
Смежные вопросы
«Структуры данных в функциональном программировании» . Мой конкретный вопрос об использовании обновлений на месте вместо неэффективных альтернатив здесь не обсуждается.
«Внутренняя мутация постоянных структур данных» . Здесь акцент, кажется, делается на низкоуровневой реализации на неопределенном языке, в то время как мой вопрос касается правильного выбора языка (функционального или иного) и возможных идиоматических решений в функциональных языках.
Соответствующие цитаты
Чисто функциональные языки программирования позволяют выразить многие алгоритмы очень кратко, но есть несколько алгоритмов, в которых обновляемое состояние на месте, кажется, играет решающую роль. Для этих алгоритмов чисто функциональные языки, в которых отсутствует обновляемое состояние, по своей природе оказываются неэффективными ( [Ponder, McGeer and Ng, 1988] ).
- Джон Лончбери и Саймон Пейтон Джонс, « Ленивые потоки функционального состояния» (1994), а также Джон Лончбери и Саймон Пейтон Джонс, « Штат в Хаскелле» (1995). Эти документы вводят ST
конструктор монадического типа в Haskell.
DiffArray
тип. Глядя на источник из diffarray пакета, я вижу 91 вхождений unsafePerformIO
. Похоже, что ответ на мой вопрос: «Да, нет, чисто функциональные языки с неизменяемыми данными не подходят для реализации алгоритмов, которые обычно полагаются на обновления на месте».
Map
, IntMap
или HashMap
) в качестве хранилища и сделать узлы содержат идентификаторы связанных узлов. «Все проблемы в информатике могут быть решены с помощью другого уровня косвенности».