Помогите мне понять, как конфликт между неизменяемостью и временем выполнения обрабатывается в Clojure.

Clojure действительно заинтересовал меня, и я начал читать учебник по нему: http://java.ociweb.com/mark/clojure/article.html

Рассмотрим эти две строки, упомянутые в разделе «Set»:

(def stooges (hash-set "Moe" "Larry" "Curly")) ; not sorted
(def more-stooges (conj stooges "Shemp")) ; -> #{"Moe" "Larry" "Curly" "Shemp"}

Моей первой мыслью было, что вторая операция должна занимать постоянное время; в противном случае функциональный язык может иметь мало преимуществ перед объектно-ориентированным. Можно легко представить себе необходимость начать с [почти] пустого набора, а затем заполнять и уменьшать его по ходу дела. Таким образом, вместо того, чтобы назначать новый результат большему количеству марионеток, мы могли бы переназначить его самому себе.

Теперь, благодаря прекрасным обещаниям функциональных языков, о побочных эффектах можно не беспокоиться. Таким образом, наборы stooges и more-stooges никогда не должны работать друг над другом. Таким образом, либо создание more-stooges является линейной операцией, либо они имеют общий буфер (как StringBuffer в Java), что кажется очень плохой идеей и конфликтует с неизменяемостью (впоследствии stooges может удалить элемент один за другим).

Я, наверное, заново изобретаю колесо здесь. кажется, что hash-set будет более производительным в clojure, когда вы начинаете с максимального количества элементов, а затем удаляете их по одному до тех пор, пока не станет пустым набором, в отличие от запуска с пустого набора и увеличения его по одному за раз.

Приведенные выше примеры могут показаться не очень практичными или иметь обходные пути, но объектно-ориентированный язык, такой как Java/C#/Python/и т. д. не имеет проблем ни с увеличением, ни с уменьшением набора по одному или нескольким элементам за раз, при этом делая это быстро.

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

Для тех, кто знаком с Python, я бы упомянул понимание множества в сравнении с эквивалентным циклическим подходом. Время работы этих двух немного отличается, но это связано с относительными скоростями C, Python, интерпретатора, а не из-за сложности. Проблема, которую я вижу, заключается в том, что понимание множества часто является лучшим подходом, но НЕ ВСЕГДА лучшим подходом, поскольку удобочитаемость может сильно пострадать.

Дайте мне знать, если вопрос не ясен.


person Hamish Grubijan    schedule 09.09.2010    source источник


Ответы (2)


Основные неизменяемые структуры данных для меня также являются одной из самых увлекательных частей языка. Ответов на этот вопрос очень много, и Рич отлично справляется с этим в этом видео:

http://blip.tv/file/707974

Основные структуры данных:

  • на самом деле полностью неизменяемы
  • старые копии также неизменны
  • производительность не ухудшается для старых копий
  • доступ постоянный (фактически ограниченный ‹= константа)
  • все поддерживают эффективное добавление, объединение (кроме списков и последовательностей) и измельчение

Как они это делают???

  • секрет: это практически все деревья под капотом (на самом деле дерево).

Но что, если я действительно хочу что-то отредактировать на месте?

  • вы можете использовать transients clojure, чтобы отредактировать структуру на месте, а затем создать неизменяемую версию (за постоянное время), когда вы готов поделиться.

в качестве небольшого фона: Trie — это дерево, в котором все общие элементы ключа подняты вверх к вершине дерева. наборы и карты в clojure используют trie, где индексы представляют собой хэш ключа, который вы ищете. затем он разбивает хэш на небольшие фрагменты и использует каждый фрагмент в качестве ключа к одному уровню хэш-три. Это позволяет совместно использовать общие части как новой, так и старой карт, а время доступа ограничено, поскольку может быть только фиксированное количество ветвей, поскольку хэш, используемый как вход, имеет фиксированный размер.

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

Я очень рекомендую (относительно короткую)_ книгу: Purely Functional Data Structures В нем он описывает множество действительно интересных структур и концепций, таких как "удаление амортизации", чтобы обеспечить постоянный доступ к очередям. и такие вещи, как ленивые постоянные очереди. автор даже предлагает бесплатную копию в pdf здесь< /а>

person Arthur Ulfeldt    schedule 09.09.2010
comment
Trei отличается от trie или это просто опечатка? - person poolie; 10.09.2010
comment
его trei (произносится как попытка) У меня просто была небольшая проблема с клавиатурой :) - person Arthur Ulfeldt; 10.09.2010
comment
Из статьи в Википедии: термин trie происходит от слова «поиск». Следуя этимологии, изобретатель Эдвард Фредкин произносит это слово как «дерево». Однако другие авторы произносят его как «попытаться». - person Aaron Novstrup; 10.09.2010

Структуры данных Clojure являются постоянными, что означает, что они неизменяемы, но используют совместное использование структур для поддержки эффективных «модификаций». См. раздел об неизменяемых структурах данных в документации Clojure для более подробного объяснения. В частности, говорится

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

Эти сообщения, а также некоторые из Доклады Рича Хикки дают хороший обзор реализации постоянных структур данных.

person Aaron Novstrup    schedule 09.09.2010
comment
Насколько сложны удаления? Предположим, у меня есть 10 разных наборов или словарей, где они создаются постепенно. И затем я удаляю узел, общий для всех 10, из самого первого набора/слова (тот, который содержит только один элемент). Что будет делать clojure и сколько времени это займет? - person Hamish Grubijan; 17.09.2010
comment
Неважно, создается набор/словарь постепенно или все сразу. Удаление так же эффективно, как и добавление — это фактически операция с постоянным временем. - person Aaron Novstrup; 18.09.2010