Обхід для виконання операцій над подвійно пов'язаними або круговими структурами даних мовами з незмінними даними


11

Я хотів би дізнатися, як скласти графіки та виконувати деякі локальні операції над ними в Haskell, але питання не є специфічним для Haskell, і замість графіків ми можемо розглядати подвійно пов'язані списки.

Запитання: Який би був ідіоматичний чи рекомендований спосіб впровадження подвійно пов'язаного списку (або іншої подвійно пов'язаної чи кругової структури даних) та операцій над ним мовою, яка в основному підтримує та виступає за незмінні структури даних (Haskell, Clojure тощо) ? Зокрема, як використовувати оновлення на місці, які формально заборонені мовою?

Я легко можу уявити, що якщо якась локальна операція виконується у подвійному зв’язку списку (якщо елемент вставляється, наприклад), можливо, не буде необхідності негайно копіювати весь список через лінь мови. Однак, оскільки список подвійно пов'язаний, якщо його змінити в одному місці, жоден із старих вузлів не може бути використаний у новій версії списку, і їх потрібно було якось позначити, скопіювати, зібрати сміття рано чи пізно . Очевидно, що це зайві операції, якщо використовуватиметься лише оновлена ​​копія списку, але вони додадуть "накладні витрати", пропорційні розміру списку.

Чи означає це, що для таких завдань незмінні дані просто недоречні, а функціональні декларативні мови без "рідної" підтримки змінних даних не такі гарні, як імперативні? Або є якийсь хитрий спосіб вирішення?

PS Я знайшов деякі статті та презентації на цю тему в Інтернеті, але важко дотримувався їх, в той час як я думаю, що відповідь на це питання не повинен мати більше одного абзацу, а може бути і діаграми ... Я маю на увазі, якщо є немає жодного "функціонального" рішення цієї проблеми, відповідь, ймовірно, "використовувати C". Якщо така є, то наскільки вона може бути складною?


Пов'язані питання


Відповідні пропозиції

Суто функціональні мови програмування дозволяють висловити багато алгоритмів дуже стисло, але є кілька алгоритмів, в яких стан оновлених даних на місці, здається, відіграє вирішальну роль. Для цих алгоритмів суто функціональні мови, яким не вистачає стану оновлення, виявляються суттєво неефективними ( [Ponder, McGeer and Ng, 1988] ).

- Джон Ленбербері та Саймон Пейтон Джонс, "Ленті функціональних державних потоків" (1994), також Джон Ленбербері та Саймон Пейтон Джонс, штат Хаскелл (1995). Ці документи представляють STконструктор монадичного типу в Хаскеллі.


4
Рекомендовано: Окасакі
Роберт Харві

2
Дякую за довідку. Я знайшов його тезу .
Олексій

Ця робота виглядає багатообіцяючою: ледачий пошук перших глибин та алгоритми лінійних графіків у Haskell (1994), Девід Кінг та Джон Лаунбері.
Олексій

Схоже, схожа проблема з масивами вирішується пакетом diffarray, який реалізує DiffArrayтип. Дивлячись на джерело з diffarray пакета, я бачу 91 входжень unsafePerformIO. Схоже, відповідь на моє запитання: «так, ні, суто функціональні мови з незмінними даними не підходять для реалізації алгоритмів, які зазвичай покладаються на оновлення на місці».
Олексій

Моє поточне рішення (в Haskell) - використовувати словник ( Map, IntMapабо HashMap) як сховище і зробити так, щоб вузли містили ідентифікатори пов'язаних вузлів. "Усі проблеми з інформатики можна вирішити іншим рівнем непрямості".
Олексій

Відповіді:


6

Можуть існувати й інші ефективні незмінні структури даних, які відповідають вашому конкретному завданню, але не такі загальні, як подвійно пов'язаний список (який, на жаль, схильний до одночасних помилок модифікації через свою мінливість). Якщо ви конкретизуєте свою проблему більш вузько, таку структуру, можливо, можна знайти.

Загальна відповідь на (відносно) економічне пересування незмінних структур - лінзи. Ідея полягає в тому, що ви можете зберегти достатньо інформації, щоб реконструювати змінену незмінну структуру з її немодифікованих частин та поточно модифікованого фрагмента та переміщатися по ній до сусіднього вузла.

Ще одна корисна структура - блискавка . (Найсмішніша частина полягає в тому, що підпис типу на блискавці об'єктива - це похідне від математики школи шкільного підпису структури.)

Ось кілька посилань.


1
залежно від того, що потрібно, блискавка також може бути корисною
jk.

Щоб уточнити мою проблему більш вузько, припустимо, я хочу запрограмувати систему переписування графіків, наприклад оцінювач обчислення лямбда на основі переписування графіків.
Олексій

1
@ Алекс: Ви знайомі з роботою "Чистих людей" над переписуванням графіків? wiki.clean.cs.ru.nl/…
Джорджіо

1
@Alexey: Не те, що я знаю: Clean - двоюрідний брат Хаскелл, який розроблявся самостійно. Він також має інший механізм боротьби з побічними ефектами (AFAIK його називають унікальними типами). З іншого боку, розробники багато працювали з переписуванням графіків. Тож вони можуть бути одними з найкращих людей, які знають як про переписування графіків, так і про функціональне програмування.
Джорджіо

1
Я погоджуюся, що, схоже, блискавка вирішує проблему із подвійно пов'язаним списком чи деревом, якщо я хочу перейти до нього та змінити місце, де я зараз перебуваю, але незрозуміло, що робити, якщо я хочу зосередити увагу на кількох місцях одночасно і, наприклад, поміняти два елементи в двох місцях далеко один від одного. Ще менш зрозуміло, чи можна це використовувати з "круглими" структурами.
Олексій

2

Haskell не перешкоджає використанню змінних структур даних. Вони сильно відлякують і ускладнюють їх використання через те, що частини коду, які їх використовують, повинні врешті повернути дію вводу-виводу (яка з часом повинна бути пов'язана з дією вводу-виводу, яка повертається основною функцією), але це не робить унеможливити використання таких структур, якщо вони вам справді потрібні.

Я б запропонував дослідити використання транзакційної пам'яті програмного забезпечення як шлях вперед. Окрім того, що забезпечує ефективний спосіб впровадження змінних конструкцій, він також забезпечує дуже корисні гарантії для безпеки потоку. Дивіться опис модуля за адресою https://hackage.haskell.org/package/stm та огляд wiki за адресою https://wiki.haskell.org/Software_transactional_memory .


Дякую, я спробую дізнатися про STM. Схоже , є ще методи в Haskell , щоб мати мінливість і стан (я натикається на MVar, State, ST), так що я буду потребувати , щоб з'ясувати їх відмінності і передбачуваного використання.
Олексій

@ Алекс: Добре, що стосується STІМО, це слід згадати у відповіді, оскільки він дозволяє запустити обчислювальні роботи, а потім викинути стан і отримати результат як чисте значення.
Джорджіо

@ Джорджо, чи можна використовувати Haskell STзі STM, щоб мати одночасно і одноразовий стан?
Олексій

Ще одна термінологічна пропозиція: складена основна дія вводу-виводу не " повертається основною функцією", а присвоюється mainзмінній. :) ( mainнавіть не виконує функції.)
Олексій

Я бачу вашу думку, але все-таки "змінна" має конотацію у свідомості більшості людей як просту цінність, а не як процес, який створює цінність, і головне явно краще розглядати як останнє, а не перше. Запропонована вами зміна, хоча є явно технічно правильною, може заплутати незнайомих із темою.
Жуль
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.