Чому списки вибраної структури даних на функціональних мовах?


45

Більшість функціональних мов використовують пов'язані списки як їх первинну незмінну структуру даних. Чому списки, а не, наприклад, дерева? Дерева також можуть повторно використовувати шляхи та навіть списки моделей.



10
@gnat - це не дублікат цього питання. Прийнята і правильна відповідь на це питання по суті "тому, що незмінний зв'язаний список дозволяє обмінюватися хвостами між оновленими та оригінальними списками", відповідь на який вказується як частина фону цього питання ...
Jules

9
Один момент уточнення полягає в тому, що "список" (концепція абстрактного програмування) відрізняється від "пов'язаного списку" (конкретна реалізація цієї концепції), подібно до того, що специфікація мови програмування відрізняється від її реалізації. Питання "чому функціональні мови використовують списки (концепція абстрактного програмування) як свою основну структуру даних?" це тонко, але принципово дуже відрізняється від питання "чому загальні реалізації функціональних мов X, Y і Z використовують пов'язані списки як свою основну структуру даних?"
RM

Я scala Vector (який реалізований як дерево) трохи віддає перевагу списку stackoverflow.com/questions/6928327/… На практиці більшість людей все ще використовують Списки (з того, що я бачив).
Акавал

Я провів деякі курси функціонального програмування у Скалі та використовував багато з деревами. Просто цей список є простішими для прикладів. Дерева використовуються для проблем з продуктивністю. Ви можете створювати непорушні дерева, використовуючи частину старих дерев так само, як ви додаєте елементи до списків, що змінюються.
Борджаб

Відповіді:


56

Тому що списки простіші за дерева. (Ви можете бачити це банально за тим, що список - це вироджене дерево, де кожен вузол має лише одну дитину.)

Список мінусів - це найпростіша можлива рекурсивна структура даних довільного розміру.

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

Зауважте, що, наприклад, у Clojure, структури даних яких були розроблені спеціально для паралельного, розподіленого, "похмурого" світу сьогодні, навіть масиви (звані вектори в Clojure), мабуть, найбільш "лінійна" структура даних з усіх насправді реалізовані як дерева.

Отже, коротше: список мінусів є найпростішою можливою стійкою рекурсивною структурою даних, і не було потреби вибирати складніший "за замовчуванням". Інші, звичайно, доступні як варіанти, наприклад, Haskell має масиви, черги пріоритетів, карти, купи, трепи, спроби та все, що ви могли собі уявити, але за замовчуванням - це простий список мінусів.


10
Так, вектори Clojure реалізовані у вигляді дерев, але слід зазначити, що вони є хеш-масивом, відображеним спробами , а не вашими стандартними бінарними data Tree a = Leaf a | Branch a (Tree a) (Tree a)s. Це підсилює ваш аргумент "простоти".
wchargin

1
Стійкі вектори FWIW Clojure реалізуються як (як @wchargin вказує на дещо складні) дерева для швидкого (логарифмічного з великою базою) доступу та оновлення довільних елементів, насправді не для паралельної роботи як такої (незмінна частина переймається цим для деяких ступінь). Інші мови FP роблять той самий вибір (іноді з різними видами дерев), коли вони хочуть обох (наприклад, Haskell Sequenceабо Scala Vector), але не використовують дерева, коли вони потребують лише читання, оскільки вони можуть досягти цього в реальному постійному часі (наприклад, Haskell's Vectorабо F # via .Net's ImmutableArray)
badcook

1
Наприклад, pmapпінг над вектором у Clojure як і раніше отримує доступ до кожного елемента послідовно; деревна структура вектора, як правило, прихована від кінцевого споживача.
badcook

5

Власне, ці списки - це дерева! У вас є вузли з двома полями carі cdr, які можуть містити більше таких вузлів або листя. Єдине , що робить ці дерева в списки, є конвенція інтерпретують по cdrпосиланню в якості посилання на наступний вузол в лінійному списку, і carпосилання в якості значення поточного вузла.

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


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

@Jules Незважаючи на це, функціональне програмування було розроблене та під сильним впливом таких мов, як LISP. Очевидно, що можна зробити всю цю ітерацію списку більш ефективною, використовуючи масиви під кришкою, або додати синтаксичний цукор, який приховує характер списку, але чисте функціональне програмування може обійтися і без цього. Крім того, Haskell є досить недавньою мовою (на 32 роки молодшою ​​за LISP), яка додає в чистий функціональний стиль зовсім небагато інших ідей, синтаксичного цукру тощо. Думаю, судити про функціональне програмування за судженнями Haskell - це трохи схоже на судження
мікерів

2
@cmaster, хоча Haskell молодший, йому ще 27 років, і він вплинув на багато мов (включаючи Python, Java та C #, щоб назвати мало впливових). Судити про функціональне програмування LISP - це як судити про імперативне програмування ALGOL - і те й інше пройшло довгий шлях, ніж стати невпізнанним. ГАРАЗД. Lisp, напевно, все ще актуальний, але я б не вважав це "мейнстрімом" і пропускає багато пізнішого розуміння, включаючи типи ML.
Мацей П'єхотка

1
Ви не можете обробляти хвостики, пов’язані окремо, рекурсивно або ітеративно без явного стека, якщо ви хочете торкнутися всього дерева, тому я не думаю, що хвоста рекурсії не має нічого спільного з цим.
k_g

@MaciejPiechotka Ні Python, Java, ні C # не можна розглядати як функціональні мови, вони вкрай необхідні та додають кілька функцій, які надихаються функціональним програмуванням. Після того, як у вас зміниться стан, ви опинитесь в області імперативного, а не функціонального програмування. Я згоден з вами, що LISP, безумовно, не є основним.
cmaster
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.