Чим відрізняються функціональні та імперативні мови програмування?


159

Більшість основних мов, включаючи об'єктно-орієнтовані мови програмування (OOP), такі як C #, Visual Basic, C ++ та Java, були розроблені для підтримки в першу чергу імперативного (процедурного) програмування, тоді як Haskell / gofer, як і мови, є суто функціональними. Чи може хтось детальніше розібратися в тому, чим відрізняється ці два способи програмування?

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



1
перевірте це інше [повідомлення] [1]. Це чітко описує відмінності. [1]: stackoverflow.com/questions/602444/…
theta

Відповіді:


160

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

Приклади: Java - імперативна мова. Наприклад, можна створити програму для додавання серії чисел:

 int total = 0;
 int number1 = 5;
 int number2 = 10;
 int number3 = 15;
 total = number1 + number2 + number3; 

Кожен оператор змінює стан програми - від призначення значень кожній змінній до остаточного додавання цих значень. За допомогою послідовності з п'яти висловлювань програма чітко розповідає, як додати числа 5, 10 і 15 разом.

Функціональні мови: Парадигма функціонального програмування була явно створена для підтримки чистого функціонального підходу до вирішення проблем. Функціональне програмування - це форма декларативного програмування.

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

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

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

Для людей з OOP або імперативних мов:

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

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

Мінуси:

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

Коли еволюція йде не так, у вас виникають проблеми:

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

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

Тож правильними способами та найкращими методами складання ретельних та перевіряються програм додатків, як правило, є розробка імперативного коду з декларативним станом душі?
Кемаль Гюлтекін

4
Я не бачу чіткої різниці в тексті, де виділяється особливість кожного програмування. Більшість описів процедурного програмування можна обміняти текстом імперативного програмування і навпаки.
AxeEffect

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

230

Ось різниця:

Обов’язкові:

  • Початок
  • Увімкніть взуття розміром 9 1/2.
  • Візьміть місце у кишені, щоб зберегти масив [7] ключів.
  • Покладіть ключі в кімнаті для ключів у кишені.
  • Введіть гараж.
  • Відкритий гараж.
  • Введіть автомобіль.

... і так далі, і далі ...

  • Поставте молоко в холодильник.
  • Стій.

Декларативний, з якого функціонал - це підкатегорія:

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

... і так далі, і далі ...

  • Переконайтеся, що у нас в холодильнику є молоко (коли воно нам потрібно - для ледачих функціональних мов).

Короткий зміст: Імперативними мовами ви розповідаєте комп'ютеру, як змінювати біти, байти та слова в пам'яті та в якому порядку. У функціональних умовах ми повідомляємо комп’ютеру, що таке речі, дії тощо. Наприклад, ми говоримо, що множина 0 дорівнює 1, а множина кожного іншого натурального числа є добутком цього числа і факторіалом його попередника. Ми не кажемо: щоб обчислити факторіал n, зарезервуйте область пам’яті та збережіть 1, а потім помножте число в цій області пам’яті з числами 2 на n і збережіть результат у тому самому місці, а в кінці, область пам'яті буде містити факториал.


1
Дякую. Це прекрасний спосіб подивитися на це.
L-Samuels

5
Мені сподобалось твоє пояснення @Igno, але щось мені досі незрозуміло. У Деклараційному документі, навіть якщо ви просто розповідаєте , але все ж вам потрібно змінити біти та внести зміни в стан машини, щоб продовжувати правильно. Мене збентежує те, що декларативне певне схоже на процедурне програмування (на зразок функцій C) , і все ж існує велика різниця між ними. Чи не є C функції такі ж, як Функції у функціональному програмуванні (на рівні машини)?
phoenisx

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

3
Це далеко не те, наскільки я розумію функціональне програмування. Я думав, що функціональне програмування - це видалення прихованих входів і виходів з функцій.
Рінго

7
Скрутне пояснення.
JoeTidee

14

Більшість сучасних мов в тій чи іншій мірі є як імперативними, так і функціональними, але для кращого розуміння функціонального програмування, найкраще буде взяти приклад чистої функціональної мови на зразок Haskell на відміну від імперативного коду не такою функціональною мовою, як java / c #. Я вважаю, що це завжди легко пояснити на прикладі, тому нижче це одне.

Функціональне програмування: обчисліть факторіал n, тобто n! тобто nx (n-1) x (n-2) x ... x 2 X 1

-- | Haskell comment goes like
-- | below 2 lines is code to calculate factorial and 3rd is it's execution  

factorial 0 = 1
factorial n = n * factorial (n - 1)
factorial 3

-- | for brevity let's call factorial as f; And x => y shows order execution left to right
-- | above executes as := f(3) as 3 x f(2) => f(2) as 2 x f(1) => f(1) as 1 x f(0) => f(0) as 1  
-- | 3 x (2 x (1 x (1)) = 6

Зауважте, що Haskel дозволяє перевантажувати функції до рівня значення аргументу. Нижче наведено приклад імперативного кодексу щодо підвищення ступеня імперативності:

//somewhat functional way
function factorial(n) {
  if(n < 1) {
     return 1;
  }
  return n * factorial(n-1);   
}
factorial(3);

//somewhat more imperative way
function imperativeFactor(n) {
  int f = 1
  for(int i = 1; i <= n; i++) {
     f = f * i
  }
  return f;
}

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

Пізніший приклад можна розглядати як грубо код java / c # lang, а перша частина - обмеження самої мови на відміну від Haskell для перевантаження функції на значення (нуль), а отже, можна сказати, що це не пуристська функціональна мова, з іншого Ви можете сказати, що це підтримує функціональний прог. певною мірою.

Розкриття інформації: жоден з наведених вище кодів не перевіряється / виконується, але, сподіваємось, він повинен бути достатньо хорошим для передачі концепції; також я вдячний за коментарі до будь-якої такої корекції :)


1
Чи не повинно бути return n * factorial(n-1);?
jinawee

@jinawee, спасибі за вказівку, я виправив це зn * (n-1)
старого ченця

10

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

Проблема: Я хочу змінити цю істоту з коня на жирафа.

  • Подовжити шию
  • Подовжити ноги
  • Нанесіть плями
  • Дайте створінню чорний язик
  • Видаліть кінський хвіст

Кожен елемент може бути запущений у будь-якому порядку, щоб отримати однаковий результат.

Імперативне програмування є процедурним. Держава і порядок важливі.

Проблема: Я хочу припаркувати свій автомобіль.

  1. Зверніть увагу на початковий стан гаражних дверей
  2. Зупинка автомобіля на проїжджій частині
  3. Якщо двері гаража закриті, відкрийте гаражні двері, пам’ятайте про новий стан; інакше продовжуйте
  4. Потягніть машину в гараж
  5. Закрийте гаражні двері

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


Я бачу лише різницю в асинхронному та синхронізованому режимі.
Володимир

@VladimirVukanac async / sync - це механізм, а не форма програмування
Jakub Keller

2
О, дякую, я буду докладніше досліджувати це. Чи будете ви таким добрим, щоб оновити проблему 1 таким же чином, як проблема 2 "Я хочу припаркувати свою машину", але написана функціональним способом програмування? Тоді паралелізм буде виключений.
Володимир

6

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

Звідси випливає, що функціональна програма - це лише вираз.

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

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

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


3

Імперативний стиль програмування практикувався в веб-розробці з 2005 року аж до 2013 року.

За допомогою імперативного програмування ми виписали код, який перераховував саме те, що має робити наша програма, крок за кроком.

Функціональний стиль програмування створює абстракцію за допомогою розумних способів поєднання функцій.

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

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

Уявіть, що ми не будуємо програмне забезпечення, а натомість печемо пиріжки на життя. Можливо, ми погані пекарі і не знаємо, як спекти смачний пиріг так, як слід.

Тож наш начальник дає нам список напрямків, які ми знаємо як рецепт.

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

  1. Змішайте 1 склянку борошна
  2. Додати 1 яйце
  3. Додати 1 склянку цукру
  4. Вилийте суміш у каструлю
  5. Поставте каструлю в духовку на 30 хвилин і 350 градусів.

Декларативний рецепт робив би наступне:

1 склянка борошна, 1 яйце, 1 склянка цукру - початковий стан

Правила

  1. Якщо все змішалося, покладіть в каструлю.
  2. Якщо все не змішано, покладіть в миску.
  3. Якщо все в каструлі, поставте в духовку.

Тож імперативні підходи характеризуються поетапними підходами. Ви починаєте з першого кроку і переходите до кроку 2 тощо.

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

У декларативному світі його різні. У декларативному рецепті ми розділимо наш рецепт на дві окремі частини, почніть з однієї частини, яка перераховує початковий стан рецепта, як і змінні. Отже, наші змінні тут - кількість наших інгредієнтів та їх тип.

Ми беремо початковий стан або початкові інгредієнти і застосовуємо до них деякі правила.

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

Тож у декларативному підході ми повинні знати, як правильно структурувати ці правила.

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

З нашим початковим станом це не відповідає, оскільки ми ще не змішали наші інгредієнти.

Отже, правило 2 говорить, якщо вони не змішалися, то перемішайте їх у мисці. Гаразд, так це правило застосовується.

Зараз у нас є миска із змішаними інгредієнтами як наша держава.

Тепер ми знову застосовуємо цю нову державу до наших правил.

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

Зараз у нас є цей новий стан, коли інгредієнти змішуються і на сковороді. Правило 1 більше не актуальне, правило 2 не застосовується.

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

І закінчуємо смачним гарячим яблучним пирогом чи чим завгодно.

Тепер, якщо ти схожий на мене, ти можеш задуматися, чому ми все ще не робимо імперативного програмування. Це має сенс.

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

У декларативному підході ми можемо мати деякі початкові інгредієнти або початковий стан, наприклад textInput=“”, одну змінну.

Можливо, введення тексту починається як порожній рядок.

Ми приймаємо цей початковий стан і застосовуємо його до набору правил, визначених у вашій заявці.

  1. Якщо користувач вводить текст, оновіть введення тексту. Ну, зараз це не стосується.

  2. Якщо шаблон наданий, обчисліть віджет.

  3. Якщо textInput оновлено, повторіть шаблон.

Ну, нічого з цього не застосовується, тому програма просто чекатиме, коли подія станеться.

Тож у якийсь момент користувач оновлює введення тексту, і тоді ми можемо застосувати правило №1.

Ми можемо оновити це до “abcd”

Таким чином, ми просто оновили наш текст та оновлення textInput, правило № 2 не застосовується, правило № 3 говорить, якщо введення тексту оновлено, що щойно відбулося, потім повторно візуалізуємо шаблон, а потім повертаємося до правила 2, що говорить, якщо шаблон зроблений , обчисліть віджет, добре давайте розрахувати віджет.

Загалом, як програмісти, ми хочемо прагнути до декларативніших програм програмування.

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


2

• Імперативні мови:

  • Ефективне виконання

  • Складна семантика

  • Складний синтаксис

  • Конкурс розроблений програмістом

  • Комплексне тестування, не має референсної прозорості, має побічні ефекти

  • Має стан

• Функціональні мови:

  • Проста семантика

  • Простий синтаксис

  • Менш ефективне виконання

  • Програми можна автоматично робити одночасно

  • Просте тестування, має референтну прозорість, не має побічних ефектів

  • Не має держави

1

Я думаю, що можливо виразити функціональне програмування в обов'язковому порядку:

  • Використання багато перевірки стану об'єктів та if... else/ switchвиписок
  • Деякий механізм очікування / очікування, щоб подбати про асинхронність

З таким підходом виникають величезні проблеми:

  • Правила / процедури повторюються
  • Державність залишає шанси на побічні ефекти / помилки

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

Приклад звичаїв: інтерфейсні додатки, такі як Android, iOS або логіка веб-додатків, включаючи спілкування з бекендом.

Інші проблеми при моделюванні функціонального програмування за допомогою імперативного / процедурного коду:

  • Стан гонки
  • Складна комбінація та послідовність подій. Наприклад, користувач намагається переслати гроші в банківській програмі. Крок 1) Виконайте все наступне паралельно, продовжуйте лише якщо все добре a) Перевірте, чи користувач все-таки хороший (шахрайство, AML) b) перевірити, чи є у користувача достатньо балансу; c) Перевірте, чи отримувач дійсний і хороший (шахрайство, Крок 2) виконання операції передачі. Крок 3) Показати оновлення балансу користувача та / або якогось відстеження. Наприклад, у RxJava код короткий і розумний. Без цього я можу уявити, що було б багато коду, безладу і схильного до помилок коду

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


-1

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

Проблема: Написання таблиці 1.

Рішення: -

За імперативним стилем: =>

    1*1=1
    1*2=2
    1*3=3
    .
    .
    .
    1*n=n 

За функціональним стилем: =>

    1
    2
    3
    .
    .
    .
    n

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

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

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