В чем разница между процедурным программированием и функциональным программированием?[закрыто]

StackOverflow https://stackoverflow.com/questions/23277

Вопрос

Я читал статьи Википедии по обоим вопросам процедурное программирование и функциональное программирование, но я все еще немного сбит с толку.Мог бы кто-нибудь довести это до конца?

Это было полезно?

Решение

Функциональный язык (в идеале) позволяет вам написать математическую функцию, т.е.функция , которая принимает n аргументы и возвращает значение.Если программа выполнена, эта функция логически вычисляется по мере необходимости.1

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

Как следствие, чисто функциональная программа всегда дает результат то же значение для входных данных и порядок оценки четко не определены;это означает, что неопределенные значения, такие как пользовательский ввод или случайные значения, трудно моделировать на чисто функциональных языках.


1 Как и все остальное в этом ответе, это обобщение.Свойство оценивать вычисление, когда необходим его результат, а не последовательно там, где оно вызывается, известно как “лень”, и не все функциональные языки на самом деле универсально ленивы, и лень не ограничивается функциональным программированием.Скорее, приведенное здесь описание обеспечивает “ментальную основу” для размышлений о различных стилях программирования, которые являются не отдельными и противоположными категориями, а скорее текучими идеями.

Другие советы

По сути, эти два стиля подобны Инь и Ян.Один из них организован, в то время как другой хаотичен.Бывают ситуации, когда функциональное программирование является очевидным выбором, а в других ситуациях лучшим выбором было процедурное программирование.Вот почему есть по крайней мере два языка, которые недавно выпустили новую версию, охватывающую оба стиля программирования. ( Perl 6 и D 2 )

Процедурный:

  • Выходные данные подпрограммы не всегда имеют прямую корреляцию с входными данными.
  • Все делается в определенном порядке.
  • Выполнение процедуры может иметь побочные эффекты.
  • Стремится акцентировать внимание на реализации решений линейным способом.

Perl 6

D 2

int factorial( int n ){

  int result = 1;

  for( ; n > 0 ; n-- ){
    result *= n;
  }

  return result;
}

Функциональный:

  • Часто рекурсивный.
  • Всегда возвращает один и тот же результат для данного ввода.
  • Порядок оценки обычно не определен.
  • Должно быть, у него нет гражданства.т. е.Никакая операция не может иметь побочных эффектов.
  • Хорошо подходит для параллельного выполнения
  • Стремится подчеркнуть подход "разделяй и властвуй".
  • Может иметь функцию отложенной оценки.

Хаскелл

( скопировано с Википедия );

fac :: Integer -> Integer

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

или в одной строке:

fac n = if n > 0 then n * fac (n-1) else 1

Perl 6

D 2

pure int factorial( invariant int n ){
  if( n <= 1 ){
    return 1;
  }else{
    return n * factorial( n-1 );
  }
}

Боковое примечание:

Факториал на самом деле является распространенным примером, показывающим, насколько легко создавать новые операторы в Perl 6 так же, как вы создали бы подпрограмму.Эта функция настолько укоренилась в Perl 6, что большинство операторов в реализации Rakudo определяются именно таким образом.Это также позволяет вам добавлять свои собственные несколько кандидатов к существующим операторам.

В этом примере также показано создание диапазона (2..$n) и мета-оператор сокращения списка ([ OPERATOR ] LIST) в сочетании с оператором числового инфиксного умножения.(*)
Это также показывает, что вы можете поместить --> UInt в подписи вместо returns UInt после этого.

( Вам может сойти с рук запуск ассортимента с помощью 2 поскольку "оператор" умножения вернет 1 при вызове без каких - либо аргументов )

Я никогда не видел этого определения, данного где-либо еще, но я думаю, что это достаточно хорошо суммирует приведенные здесь различия:

Функциональный программирование фокусируется на выражения

Процедурный программирование фокусируется на заявления

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

Операторы не имеют значений и вместо этого изменяют состояние некоторой концептуальной машины.

В чисто функциональном языке не было бы операторов в том смысле, что нет способа манипулировать состоянием (у них все еще может быть синтаксическая конструкция с именем "statement", но если она не манипулирует состоянием, я бы не назвал это заявлением в этом смысле).На чисто процедурном языке не было бы выражений, все было бы инструкцией, которая манипулирует состоянием машины.

Haskell был бы примером чисто функционального языка, потому что нет способа манипулировать состоянием.Машинный код был бы примером чисто процедурного языка, потому что все в программе является оператором, который манипулирует состоянием регистров и памяти машины.

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

Например, C был бы более функциональным, чем COBOL, потому что вызов функции - это выражение, тогда как вызов подпрограммы в COBOL - это оператор (который манипулирует состоянием совместно используемых переменных и не возвращает значения).Python был бы более функциональным, чем C, потому что он позволяет вам выражать условную логику в виде выражения с использованием вычисления короткого замыкания (test && path1 || path2 в отличие от операторов if).Scheme была бы более функциональной, чем Python, потому что все в scheme является выражением.

Вы все еще можете писать в функциональном стиле на языке, который поощряет процедурную парадигму, и наоборот.Просто сложнее и / или более неуклюже писать в парадигме, которая не поощряется языком.

В информатике функциональное программирование - это парадигма программирования, которая рассматривает вычисления как оценку математических функций и избегает состояния и изменяемых данных.Он делает акцент на применении функций, в отличие от процедурного стиля программирования, который делает акцент на изменениях состояния.

Я считаю, что процедурное / функциональное / объективное программирование - это то, как подойти к проблеме.

Первый стиль планировал бы все по шагам и решал проблему, реализуя по одному шагу (процедуре) за раз.С другой стороны, функциональное программирование сделало бы упор на подход "разделяй и властвуй", при котором проблема делится на подзадачи, затем решается каждая подзадача (создается функция для решения этой подзадачи) и результаты объединяются, чтобы создать ответ для всей проблемы.Наконец, объективное программирование имитировало бы реальный мир, создавая мини-мир внутри компьютера со множеством объектов, каждый из которых обладает (в некоторой степени) уникальными характеристиками и взаимодействует с другими.В результате этих взаимодействий появился бы результат.

Каждый стиль программирования имеет свои преимущества и недостатки.Следовательно, делать что-то вроде "чистого программирования" (т.е.чисто процедурно (кстати, никто этого не делает, что немного странно - или чисто функционально, или чисто объективно) очень сложно, если не невозможно, за исключением некоторых элементарных задач, специально разработанных для демонстрации преимущества стиля программирования (поэтому мы называем тех, кто любит чистоту, "weenie" : D).

Затем, из этих стилей, у нас есть языки программирования, которые разработаны таким образом, чтобы оптимизировать некоторые из них для каждого стиля.Например, Сборка - это все, что связано с процедурой.Ладно, большинство ранних языков процедурные, не только Asm, такие как C, Pascal (и Fortran, я слышал).Затем у нас есть всем известная Java в objective school (на самом деле, Java и C # также входят в класс под названием "ориентированный на деньги", но это тема для другого обсуждения).Также целью является Smalltalk.В функциональной школе у нас были бы "почти функциональные" (некоторые считали их нечистыми) Семейства Lisp и ML, а также многие "чисто функциональные" Haskell, Erlang и т.д.Кстати, существует много общих языков, таких как Perl, Python, Ruby.

Подробнее о комментарии Конрада:

Как следствие, чисто функциональная программа всегда выдает одно и то же значение для входных данных, а порядок вычисления четко не определен;

Из-за этого функциональный код, как правило, легче распараллеливать.Поскольку (как правило) у функций нет побочных эффектов, и они (как правило) просто воздействуют на свои аргументы, многие проблемы параллелизма исчезают.

Функциональное программирование также используется, когда вам нужно быть способным доказывающий ваш код верен.Это гораздо сложнее сделать с процедурным программированием (непросто с функциональным, но все же проще).

Отказ от ответственности:Я уже много лет не пользовался функциональным программированием и только недавно начал изучать его снова, так что, возможно, я здесь не совсем прав.:)

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

fac n = foldr (*) 1 [1..n]

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

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

Процедурные языки, как правило, отслеживают состояние (используя переменные) и, как правило, выполняются в виде последовательности шагов.Чисто функциональные языки не отслеживают состояние, используют неизменяемые значения и, как правило, выполняются как ряд зависимостей.Во многих случаях состояние стека вызовов будет содержать информацию, которая была бы эквивалентна той, которая была бы сохранена в переменных состояния в процедурном коде.

Рекурсия - классический пример программирования в функциональном стиле.

Сказал Конрад:

Как следствие, чисто функциональная программа всегда выдает одно и то же значение для входных данных, и порядок вычисления четко не определен;это означает, что неопределенные значения, такие как пользовательский ввод или случайные значения, трудно моделировать на чисто функциональных языках.

Порядок оценки в чисто функциональной программе может быть сложным (er) для рассуждений (особенно из-за лени) или даже неважным, но я думаю, что, говоря, что он недостаточно четко определен, звучит так, будто вы не можете сказать, будет ли ваша программа работать вообще!

Возможно, лучшим объяснением было бы то, что поток управления в функциональных программах основан на том, когда требуется значение аргументов функции.Хорошая вещь в этом заключается в том, что в хорошо написанных программах состояние становится явным:каждая функция перечисляет свои входные данные как параметры, а не произвольно жующий глобальное состояние.Так что на каком - то уровне, проще рассуждать о порядке вычисления по отношению к одной функции за раз.Каждая функция может игнорировать остальную часть вселенной и сосредоточиться на том, что ей нужно делать.При объединении функции гарантированно будут работать так же [1], как и по отдельности.

...неопределенные значения, как пользовательский ввод или случайные значения трудно смоделировать в чисто функциональные языки.

Решение проблемы ввода в чисто функциональных программах состоит в том, чтобы встроить императивный язык в виде DSL используя достаточно мощная абстракция.В императивных (или не чисто функциональных) языках это не требуется, потому что вы можете "обмануть" и передать состояние неявно, а порядок вычисления является явным (нравится вам это или нет).Из-за этого "обмана" и принудительной оценки всех параметров для каждой функции в императивных языках 1) вы теряете возможность создавать свои собственные механизмы потока управления (без макросов), 2) код по своей сути не является потокобезопасным и / или распараллеливаемым по умолчанию, 3) и реализация чего-то вроде отмены (путешествия во времени) требует тщательной работы (императивный программист должен хранить рецепт для возврата старых значений!), тогда как чистое функциональное программирование дает вам все эти вещи — и еще несколько, которые я, возможно, забыл — "бесплатно".

Я надеюсь, это не звучит как фанатизм, я просто хотел добавить немного перспективы.Императивное программирование и особенно программирование со смешанной парадигмой на мощных языках, таких как C # 3.0, по-прежнему являются абсолютно эффективными способами достижения цели и серебряной пули не существует.

[1] ...за исключением, возможно, использования памяти (см.foldl и foldl' в Haskell).

Подробнее о комментарии Конрада:

и порядок оценки не является четко определенным

Некоторые функциональные языки имеют то, что называется отложенным вычислением.Это означает, что функция не выполняется до тех пор, пока не потребуется значение.До этого времени передается сама функция.

Процедурные языки - это шаг 1, шаг 2, шаг 3...если на шаге 2 вы скажете добавить 2 + 2, он сделает это прямо сейчас.При ленивой оценке вы бы сказали добавить 2 + 2, но если результат никогда не используется, он никогда не выполняет сложение.

Если у вас есть возможность, я бы порекомендовал получить копию Lisp / Scheme и выполнить в ней несколько проектов.Большинство идей, которые в последнее время стали популярными, были выражены на лиспе десятилетия назад:функциональное программирование, продолжения (в виде замыканий), сборка мусора, даже XML.

Так что это был бы хороший способ получить фору по всем этим текущим идеям и еще нескольким помимо них, таким как символьные вычисления.

Вы должны знать, для чего полезно функциональное программирование, а для чего оно не подходит.Это полезно не для всего.Некоторые проблемы лучше всего выразить в терминах побочных эффектов, когда один и тот же вопрос дает разные ответы в зависимости от того, когда он задан.

@Крейтон:

В Haskell есть библиотечная функция, вызываемая продукт:

prouduct list = foldr 1 (*) list

или просто:

product = foldr 1 (*)

итак, "идиоматический" факториал

fac n = foldr 1 (*)  [1..n]

было бы просто

fac n = product [1..n]

Функциональное Программирование

num = 1 
def function_to_add_one(num):
    num += 1
    return num


function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)

#Final Output: 2

Процедурное программирование

num = 1 
def procedure_to_add_one():
    global num
    num += 1
    return num


procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()

#Final Output: 6

function_to_add_one является функцией

procedure_to_add_one это процедура

Даже если вы запустите функция пять раз, и каждый раз это будет возвращаться 2

Если вы запустите процедура пять раз, в конце пятого прогона это даст вам 6.

Процедурное программирование разделяет последовательности операторов и условных конструкций на отдельные блоки, называемые процедурами, которые параметризуются по аргументам, являющимся (нефункциональными) значениями.

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

Обратите внимание, что функциональное программирование является обобщением процедурного программирования в данной интерпретации.Однако меньшинство интерпретирует "функциональное программирование" как "без побочных эффектов", что совсем другое, но не имеет отношения ко всем основным функциональным языкам, кроме Haskell.

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

По сути, процедурное программирование - это просто способ структурирования императивных программ, в которых основным методом абстракции является "процедура". (или "функция" в некоторых языках программирования).Даже объектно-ориентированное программирование - это просто еще один способ структурирования императивной программы, где состояние инкапсулируется в объекты, становясь объектом с "текущим состоянием", плюс у этого объекта есть набор функций, методов и прочего, что позволяет программисту манипулировать состоянием или обновлять его.

Теперь, что касается функционального программирования, то суть суть его подхода заключается в том, что он определяет, какие значения следует принимать и как эти значения должны быть переданы.(таким образом, нет ни состояния, ни изменяемых данных, поскольку он принимает функции как значения первого класса и передает их в качестве параметров другим функциям).

PS:понимание каждой используемой парадигмы программирования должно прояснить различия между ними всеми.

PSS:В конце концов, парадигмы программирования - это просто разные подходы к решению проблем.

PSS: это в ответе quora есть отличное объяснение.

Ни один из приведенных здесь ответов не показывает идиоматического функционального программирования.Рекурсивный факториальный ответ отлично подходит для представления рекурсии в FP, но большая часть кода не является рекурсивной, поэтому я не думаю, что этот ответ является полностью репрезентативным.

Допустим, у вас есть массив строк, и каждая строка представляет целое число типа "5" или "-200".Вы хотите проверить этот входной массив строк на соответствие вашему внутреннему тестовому варианту (используя сравнение целых чисел).Оба решения показаны ниже

Процедурный

arr_equal(a : [Int], b : [Str]) -> Bool {
    if(a.len != b.len) {
        return false;
    }

    bool ret = true;
    for( int i = 0; i < a.len /* Optimized with && ret*/; i++ ) {
        int a_int = a[i];
        int b_int = parseInt(b[i]);
        ret &= a_int == b_int;  
    }
    return ret;
}

Функциональный

eq = i, j => i == j # This is usually a built-in
toInt = i => parseInt(i) # Of course, parseInt === toInt here, but this is for visualization

arr_equal(a : [Int], b : [Str]) -> Bool =
    zip(a, b.map(toInt)) # Combines into [Int, Int]
   .map(eq)
   .reduce(true, (i, j) => i && j) # Start with true, and continuously && it with each value

В то время как чисто функциональные языки, как правило, являются исследовательскими языками (поскольку реальный мир любит бесплатные побочные эффекты), реальные процедурные языки будут использовать гораздо более простой функциональный синтаксис, когда это уместно.

Обычно это реализуется с помощью внешней библиотеки, такой как Lodash, или доступно встроенное с более новыми языками, такими как Ржавчина.Тяжелая работа по функциональному программированию выполняется с помощью таких функций / концепций, как map, filter, reduce, currying, partial, последние три из которых вы можете просмотреть для дальнейшего понимания.

Добавление

Для того чтобы использоваться в обычном режиме, компилятору обычно приходится решать, как внутренне преобразовать функциональную версию в процедурную, поскольку накладные расходы на вызов функции слишком высоки.В рекурсивных случаях, таких как показанный факториал, будут использоваться такие приемы, как вызов хвоста чтобы исключить использование O (n) памяти.Тот факт, что отсутствуют побочные эффекты, позволяет функциональным компиляторам реализовывать && ret оптимизация, даже когда .reduce делается в последнюю очередь.Использование Lodash в JS, очевидно, не допускает никакой оптимизации, так что это удар по производительности (что обычно не является проблемой при веб-разработке).Языки, такие как Rust, будут оптимизироваться внутренне (И иметь такие функции, как try_fold чтобы помочь && ret оптимизация).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top