Чому Python не дуже хороший для функціонального програмування? [зачинено]


324

Я завжди думав, що функціональне програмування можна робити в Python. Таким чином, я був здивований, що Python не отримав особливої ​​згадки в цьому питанні, і коли це було згадано, зазвичай це було не дуже позитивно. Однак для цього було не так багато причин (згадувалося про відсутність відповідності шаблонів та типів алгебраїчних даних). Отже, моє питання: чому Python не дуже хороший для функціонального програмування? Чи є більше причин, ніж її відсутність відповідності шаблонів та алгебраїчних типів даних? Або ці поняття настільки важливі для функціонального програмування, що мова, яка їх не підтримує, може бути класифікована лише як функціональна мова програмування другого рівня? (Майте на увазі, що мій досвід функціонального програмування досить обмежений.)


2
2018 - Кокос (функціональна мова програмування, що компілюється на Python) розширює функціональне програмування в Python. Дивіться також серію статей з IBM стр.1 стор.2 PAGE3
cssyphus

Відповіді:


393

Питання, на яке ви посилаєтеся, задає, які мови сприяють як OO, так і функціональному програмуванню. Python не сприяє функціональному програмуванню, хоча він працює досить добре.

Найкращий аргумент проти функціонального програмування в Python - це те, що випадки використання імперативних / OO ретельно розглядаються Guido, тоді як випадки використання функціонального програмування - ні. Коли я пишу імперативний Python, це одна з найкрасивіших мов, яку я знаю. Коли я пишу функціональний Python, він стає таким же потворним і неприємним, як і ваша середня мова, яка не має BDFL .

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

Ось функціональні речі, які мені не вистачає в Python:


  • Не узгодження шаблону і відсутність хвостової рекурсії означають, що ваші основні алгоритми повинні бути написані обов'язково. Рекурсія в Python негарна і повільна.
  • Невелика бібліотека списків і жодні функціональні словники означають, що вам доведеться писати багато матеріалів самостійно.
  • Немає синтаксису для currying або композиції означає, що стиль без точок приблизно такий же повний пунктуації, як явно передані аргументи.
  • Ітератори замість ледачих списків означають, що ви повинні знати, чи хочете ви ефективності чи наполегливості, і розкидати дзвінки listнавколо, якщо хочете наполегливості. (Ітератори використовують один раз)
  • Простий імперативний синтаксис Python, а також його простий парсер LL1, означають, що кращий синтаксис для if-виразів та лямбда-виразів в основному неможливий. Гвідо так подобається, і я думаю, він має рацію.

5
+1 за відсутністю хвостової рекурсії - хоч циклічні конструкції її витіснили, але все-таки щось не варте пройти між Python та Scheme.
new123456

5
Відмінна відповідь щодо повноти та композиції. На жаль, подібно до такої кількості відповідей із сильним функціональним фоном, термінологія ММО жорстоко використовує термін. Хоча я розумію, що ви не можете розробити кожну концепцію у відповіді, мені цікаво, чи ОП (з допущеним обмеженим фоном FP) добре поінформоване, читаючи такі терміни, як "відповідність шаблону", "функціональні словники", "каррі" або " ліниві списки ".
ThomasH

4
Гарна думка; Я думаю, що рішення - додати посилання. Чи вистачає представника для редагування моєї відповіді? Якщо так, сміливо додайте посилання на різні концепції. Почну, коли матиму час пізніше.
Натан Шивелі-Сандерс

5
Я усвідомлюю, що цьому вже 5 років, але ... це здається, що це більше про речі, які ви сумуєте з Haskell , а не з функціональних мов . Наприклад, більшість діалектів та нащадків ML та Lisp не мають автоматичного currying, роблять безточний стиль надмірно багатослівним, не мають лінивих списків тощо. Отже, якщо ітератори замість ледачих списків робить Python поганою функціональною мовою, маючи і не повинно зробити CaML жахливою функціональною мовою?
abarnert

4
@abarnert: У Caml є будь-яка точка кулі, окрім ледачих списків, які доступні як бібліотека. Я час від часу використовував Caml під час написання цієї відповіді і зараз використовую F #. Вони обидва дуже приємні функціональні мови.
Натан Шивелі-Сандерс

102

Guido має гарне пояснення цього тут . Ось найбільш відповідна частина:

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

...

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

Нарешті, незважаючи на те, що впродовж багатьох років було введено ряд функціональних функцій програмування, Python все ще не має певних функцій, що знаходяться у «реальних» функціональних мовах програмування. Наприклад, Python не виконує певних видів оптимізації (наприклад, хвостова рекурсія). Взагалі, оскільки надзвичайно динамічний характер Python, неможливо здійснити оптимізацію часу компіляції, відому з таких функціональних мов, як Haskell або ML. І це добре.

Я витягаю з цього дві речі:

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

8
Ви можете зробити оптимізацію хвостових дзвінків у Python просто чудово. Гвідо цього не розуміє / не розуміє.
Жуль

26
Здається, зводиться до того, що Гідо ван Россум не любить функціональний стиль.
Svante

59
Я думаю, що точніше сказати, що Гвідо ван Россум не розуміє функціонального стилю і не розуміє, чому Python їм потрібен. Ви повинні зрозуміти дві речі: 1) мови програмування знаходяться в нижній частині технологічного стеку і впливають на все, побудоване на них, і 2) так само, як і в будь-який інший фрагмент програмного забезпечення, простіше додавати функції, ніж видаляти їх. Тож я думаю, що дизайнеру мови є хороша якість критично ставитися до таких запитів.
Джейсон Бейкер

8
"Звичайно, Лісп настільки ж динамічний" -> і так само необхідний!
піон

6
@Jules, Ви не проти поділитися керівництвом щодо використання оптимізації хвостових викликів у Python? Вказівник на якесь джерело було б корисним.
Девід Шейк

52

Схема не містить алгебраїчних типів даних або відповідності шаблонів, але це, безумовно, функціональна мова. Дратівливі речі про Python з точки зору функціонального програмування:

  1. Каліка лямбда. Оскільки Ламбдас може містити лише вираз, і ви не можете все зробити так легко в контексті вираження, це означає, що функції, які ви можете визначити "на льоту", обмежені.

  2. Якщо є висловлюваннями, а не виразами. Це означає, серед іншого, що ви не можете мати лямбда з символом If всередині нього. (Це виправлено тернарями в Python 2.5, але це виглядає некрасиво.)

  3. Гвідо погрожує видалити карту, фільтрувати та зменшувати її раз у раз

З іншого боку, python має лексичні закриття, лямбдаси та списки (що є дійсно "функціональним" поняттям, визнає чи ні Guido це). Я дуже багато програмувань у функціональному стилі на Python, але навряд чи можу сказати, що це ідеально.


3
Карта, фільтр та зменшення дійсно не потрібні в python. Я ще не бачив фрагмента коду, який дуже спростили їх використання. Крім того, виклик функцій в Python може бути дорогим, тому зазвичай краще просто використовувати список / генератор або зрозуміти цикл.
Джейсон Бейкер

2
Саме так говорить Натан Сандерс нижче: "Python не сприяє функціональному програмуванню, хоча він працює досить добре". Якби Гуїдо хотів, щоб Python став функціональною мовою, він зробив би достатньо добре функціонування, щоб використовувати функції викидання, і скасував би Lambdas в тій мірі, в якій можна реально використовувати карту / фільтр / зменшити корисними способами. З іншого боку, функціональні люди починають прокидатися до дивовижних розумінь списку. Сподіваємось, нам не доведеться вибирати те чи інше.
Яків Б

7
карта та фільтр тривіально замінюються розумінням списку. зменшити - майже завжди - воно настільки неефективно, що його слід замінити функцією генератора.
S.Lott

13
@ S.Lott як ти заміниш зменшення генератором?
Сурма

17
@JacobB Розуміння списку було доступне у функціональних мовах приблизно за 15 років до винайдення Python та за 25 років до того, як Python отримав реалізацію цієї функції. Думка про те, що Python вплинув на їх розповсюдження, або що fp дізнався про це від Python, або навіть просто про те, що його популярність у світі fp після дати реалізації Python є просто помилковою. Реалізація Python була взята безпосередньо з Haskell. Можливо, я вас зрозумів неправильно, і це не те, що ви мали на увазі, але мене вражає "функціональні люди починають прокидатися до дивовижних розумінь списку".
йогобрюс

23

Я б ніколи не називав Python "функціональним", але всякий раз, коли я програмую в Python, код незмінно закінчується практично чисто функціональним.

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


17

Дозвольте мені продемонструвати фрагмент коду, взятого з відповіді на "функціональне" питання Python на SO

Пітон:

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

Haskell:

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

Основна відмінність в тому , що стандартна бібліотека Haskell має корисні функції для функціонального програмування: в цьому випадку iterate, concatі(!!)


7
Ось заміна одна лінія для grandKids()тіла з виразами генератора: return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Льоекі

6
А ось і те, що не потрібно concat:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki

9
@Lloeki: ітерація значно спростить цей код, а (x for v in a для x у kidsFunc (v)) набагато чіткіше, ніж concatMap (kidsFunc). Відсутність в Python приємних вбудованих вищих порядків робить еквівалентний код виразним і багатослівним порівняно з Haskell.
Phob

2
concat можна замінити наitertools.chain.from_iterable
Сурма

@Свідчення: добре знати. thx
yairchu

14

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

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

Функціональне програмування - це багато чого; ви нічого не змінюєте, ви просто оцінюєте рівняння (або в цьому випадку "програму") і з'ясовуєте, що відповідає. Програма все ще є, немодифікована. Те саме з даними.

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

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

Тепер, якщо ви проходите через python і перевіряєте, наскільки добре підтримує мову та бібліотеки ці гарантії ці аспекти - тоді ви вже готові відповісти на власне питання.


2
Функції першокласні в Python.
Карл Сміт

@CarlSmith Цей, але залишає 3/4, що у Python не має. : - \
arya

1
Я не думаю, що Python є гарною мовою для функціонального програмування. Я навіть зараз не впевнений, що я мав на увазі під цим коментарем, щоб бути чесним. Це не здається відповідним до відповіді. Я б її видалив, але тоді ваш коментар буде поза контекстом.
Карл Сміт

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

Крім того, Python має підтримку як для curry, так і для складу, але не на мовному рівні, натомість він знаходиться в стандартній бібліотеці.
Кевін

10

Python - це майже функціональна мова. Це "функціональна літера".

Він має додаткові можливості, тому для деяких він недостатньо чистий.

У ньому також відсутні деякі функції, тому для деяких він недостатньо повний.

Пропущені функції досить легко написати. Перевірте повідомлення , як це на FP в Python.


2
Здебільшого я згоден з цією посадою. Але я не можу погодитися з тим, що Python - це функціональна мова. Це дуже заохочує імперативне програмування, і важко не мати згаданих "зайвих функцій". Я думаю, що найкраще позначати Python як "функціональну літературу", як ви робили в іншій посаді. :-)
Джейсон Бейкер

8
-1 Вибачте, ні. Я маю на увазі, просто, ні. Функціональні мови надають конструкції, що полегшують формальне міркування: індуктивне (ML), рівняльне (Haskell). Закриття та анонімні функції лише є синтаксичним цукром для стратегії.
піон

8

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


4

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

У будь-якому випадку виявляється, що map( foo() , x ) * map( foo(), y )це те саме, щоmap( foo(), x * y ) . Останній випадок насправді швидший, ніж перший, оскільки перший виконує дві копії, коли другий виконує один.

Краще функціональні мови розпізнають ці математично засновані зв’язки і автоматично виконують оптимізацію. Мови, не присвячені функціональній парадигмі, швидше за все, не оптимізуються.


map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )не вірно для всіх функцій. Наприклад, розглянемо випадок, коли fooобчислює похідну.
Елі Корвіго

1
Я думаю, що він мав на увазі +замість цього *.
користувач1747134

Ви припускаєте, що foo () є лінійним?
Джуан Ісаза

це властивість НЕ вірно для foo (x) = x + 1. As (x + 1) * (y + 1)! = X * y + 1.
juan Ісаза
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.