Вирішення проблем держави у функціональному програмуванні


18

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


4
Перший крок, ймовірно, усвідомлення того, що проблеми по суті не є важливими.
Теластин

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

4
За винятком, мабуть, прикладу бази даних, ці проблеми не є суттєвими. Наприклад, для програмування графічного інтерфейсу ви дійсно використовуєте стан, що змінюється, як погану, неявну модель часу ; функціональне реактивне програмування дозволяє чітко моделювати час, не покладаючись на стан, надаючи потоки подій, які можна поєднувати.
Тихон Єлвіс

1
Є більш просте рішення: коли ви стикаєтеся з проблемою, яка не легко моделюється методами FP, не використовуйте функціональне програмування для її вирішення. Правильний інструмент для роботи та всього цього ...
Мейсон Уілер,

1
@AndrewMartin Не хороша модель реального світу? Математика, що використовується у фізиці для моделювання реального світу, суто функціональна. При хорошому сміттєзбірнику виділення предмета коштує так само дешево, як і зіткнення вказівника, а час збору пропорційний кількості живих об’єктів. У будь-якому випадку, я б став на обмін, що основним джерелом неефективності функціонального програмування є використання структур даних, які не є кешовими. Пов'язані списки та двійкові дерева не є точно дочірніми ефективністю кешу.
Doval

Відповіді:


20

Функціональні програми дуже добре обробляють стан, але вимагають іншого погляду на це. Для прикладу вашої позиції слід врахувати одне, що ваша позиція є функцією часу замість фіксованого значення . Це добре спрацьовує для частинок, що слідують за фіксованим математичним шляхом, але вам потрібна інша стратегія поводження зі зміною шляху, наприклад, після зіткнення.

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


5
+1 Це нормально мати стан у FP, тільки не змінювати стан.
jhewlett

1
Дякую за це розуміння. Мої турботи щодо неефективності зірвали @logc; технічні деталі перетворення держави - це проблема низького рівня реалізації, яку повинна вирішити сама мова. Я спостерігав, як Річ Хікі пояснює, як він це робить з Clojure у відео.
Ендрю Мартін

1
@jhewlett: Якщо бути точнішим: FP має стан, навіть змінний стан, але вони не представляють його за допомогою змінних змінних.
Джорджіо

9

Як зазначає @KarlBielefeldt, функціональним підходом до такої проблеми є розгляд її як повернення нового стану з попереднього стану. Самі функції не містять ніякої інформації, тому вони завжди оновлюватимуть стан m до стану n .

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

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

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

З урахуванням сказаного, навіть у Хаскелл існує державна монада, яка допомагає моделювати ситуації, коли "утримання держави" є більш інтуїтивним рішенням проблеми. Але, будь ласка, також зауважте, що деякі проблеми, які, як ви вважаєте, за своєю сутністю, як запис у базу даних, мають такі незмінні рішення, як Datomic . Це може дивувати, поки ви не зрозумієте, що це концепція, не обов'язково її реалізація.


4
Я думаю, що фрагмент про оновлення великого списку вводить в оману; Я не знаю жодного компілятора, який би реально здійснив оптимізацію для вас. Навіть якщо компілятор міг би це зробити, це можливо лише у випадках, коли ви не тримаєтесь на попередніх версіях списку. Реальним вирішенням цієї проблеми є використання структури списку даних , яка не вимагає копіювання все , що потрібно змінити один елемент.
Doval

@Doval: "Навіть якщо компілятор міг би це зробити, це можливо лише у випадках, коли ви не тримаєтесь на попередніх версіях списку.": Це нагадує мені про унікальні типи в "Очистити".
Джорджіо

4

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

Багатий Хікі висвітлює ці ідеї:

Є й інші переговори, але це має направити вас у правильному напрямку.


3

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

Класи / структури даних у розділі станів зберігають дані програми та функції цього розділу працюють із неявними знаннями даних програми.

Класи / структури даних / функції в розділі без апарату є для підтримки суто алгоритмічних аспектів програми. Вони не мають явних знань про дані програми. Вони працюють суто функціонально. Складові частини програми можуть спричинити зміну стану як побічний ефект запущених функцій у розділі програми без стану.

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


Як це відповідає на питання? (not-downvoting)
kravemir

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