Мне действительно сложно понять разницу между парадигмами процедурного и функционального программирования.
Вот первые два абзаца из статьи Википедии о функциональном программировании :
В информатике функциональное программирование - это парадигма программирования, которая рассматривает вычисления как оценку математических функций и избегает состояния и изменяемых данных. Он подчеркивает применение функций, в отличие от императивного стиля программирования, который подчеркивает изменения в состоянии. Функциональное программирование уходит корнями в лямбда-исчисление, формальную систему, разработанную в 1930-х годах для исследования определения функции, ее применения и рекурсии. Многие языки функционального программирования можно рассматривать как развитие лямбда-исчисления.
На практике разница между математической функцией и понятием «функция», используемым в императивном программировании, заключается в том, что императивные функции могут иметь побочные эффекты, изменяя значение состояния программы. Из-за этого им не хватает ссылочной прозрачности, т. Е. Одно и то же языковое выражение может приводить к различным значениям в разное время в зависимости от состояния выполняющейся программы. И наоборот, в функциональном коде выходное значение функции зависит только от аргументов, которые вводятся в функцию, поэтому
f
двойной вызов функции с одним и тем же значением для аргументаx
приведет к тому же результату.f(x)
оба раза. Устранение побочных эффектов может значительно облегчить понимание и прогнозирование поведения программы, что является одной из ключевых мотиваций для разработки функционального программирования.
В пункте 2, где говорится
И наоборот, в функциональном коде выходное значение функции зависит только от аргументов, которые вводятся в функцию, поэтому
f
двойной вызов функции с одним и тем же значением для аргументаx
приведет к одинаковому результатуf(x)
оба раза.
Разве это не тот же самый случай процедурного программирования?
На что следует обратить внимание в отличии процедурного от функционального?
Ответы:
Функциональное программирование
Функциональное программирование относится к способности рассматривать функции как значения.
Рассмотрим аналогию с «обычными» значениями. Мы можем взять два целых значения и объединить их с помощью
+
оператора, чтобы получить новое целое число. Или мы можем умножить целое число на число с плавающей запятой, чтобы получить число с плавающей запятой.В функциональном программировании мы можем комбинировать два значения функции для создания нового значения функции, используя такие операторы, как compose или lift . Или мы можем объединить значение функции и значение данных для создания нового значения данных, используя такие операторы, как map или fold .
Обратите внимание, что многие языки обладают возможностями функционального программирования - даже языки, которые обычно не считаются функциональными языками. Даже дедушка FORTRAN поддерживал значения функций, хотя и не предлагал много способов объединения функций. Чтобы язык назывался «функциональным», он должен в значительной степени охватывать возможности функционального программирования.
Процедурное программирование
Под процедурным программированием понимается способность инкапсулировать общую последовательность инструкций в процедуру, чтобы эти инструкции можно было вызывать из многих мест, не прибегая к копированию и вставке. Поскольку процедуры были очень ранней разработкой в программировании, возможности почти всегда связаны со стилем программирования, требуемым программированием на машинном языке или языке ассемблера: стилем, который подчеркивает понятие мест хранения и инструкций, которые перемещают данные между этими местами.
Контраст
Эти два стиля на самом деле не являются противоположностями - они просто отличаются друг от друга. Есть языки, которые полностью охватывают оба стиля (например, LISP). Следующий сценарий может дать представление о некоторых различиях в двух стилях. Давайте напишем код для бессмысленного требования, в котором мы хотим определить, все ли слова в списке содержат нечетное количество символов. Во-первых, процедурный стиль:
Считаю само собой разумеющимся, что этот пример понятен. Теперь о функциональном стиле:
Работая изнутри, это определение выполняет следующие функции:
compose(odd, length)
сочетает в себеodd
иlength
функции , чтобы произвести новую функцию , которая определяет , является ли длина строки нечетно.map(..., words)
вызывает эту новую функцию для каждого элемента вwords
, в конечном итоге возвращая новый список логических значений, каждое из которых указывает, имеет ли соответствующее слово нечетное количество символов.apply(and, ...)
применяет к полученному списку оператор «и» и объединяет все логические значения вместе, чтобы получить окончательный результат.Из этих примеров видно, что процедурное программирование очень заинтересовано в перемещении значений в переменных и явном описании операций, необходимых для получения конечного результата. Напротив, функциональный стиль подчеркивает комбинацию функций, необходимых для преобразования исходного ввода в конечный результат.
В примере также показаны типичные относительные размеры процедурного и функционального кода. Более того, это демонстрирует, что характеристики производительности процедурного кода легче увидеть, чем характеристики функционального кода. Подумайте: вычисляют ли функции длины всех слов в списке или каждое останавливается сразу после нахождения первого слова четной длины? С другой стороны, функциональный код позволяет качественной реализации выполнить довольно серьезную оптимизацию, поскольку он в первую очередь выражает намерение, а не явный алгоритм.
Дальнейшее чтение
Этот вопрос возникает часто ... см., Например:
Лекция Джона Бэкуса о награждении Тьюринга очень подробно раскрывает мотивы функционального программирования:
Можно ли освободить программирование от стиля фон Неймана?
Я действительно не должен упоминать эту статью в данном контексте, потому что она довольно быстро становится технической. Я просто не мог устоять, потому что считаю это основополагающим.
Приложение - 2013
Комментаторы отмечают, что популярные современные языки предлагают другие стили программирования помимо процедурных и функциональных. Такие языки часто предлагают один или несколько из следующих стилей программирования:
В комментариях ниже приведены примеры того, как примеры псевдокода в этом ответе могут извлечь выгоду из некоторых возможностей, доступных из этих других стилей. В частности, процедурный пример выиграет от применения практически любой конструкции более высокого уровня.
Представленные примеры намеренно избегают смешивания этих других стилей программирования, чтобы подчеркнуть различие между двумя обсуждаемыми стилями.
источник
odd_words(words)
определение отличается от ответаallOdd
. Для фильтрации и сопоставления часто предпочтительнее составление списков, но здесьallOdd
предполагается, что функция сокращает список слов до одного логического значения.Настоящая разница между функциональным и императивным программированием заключается в мировоззрении: императивные программисты думают о переменных и блоках памяти, в то время как функциональные программисты думают: «Как мне преобразовать свои входные данные в выходные данные?» - ваша «программа» - это конвейер и набор преобразований данных, чтобы перевести их из входа в выход. ИМО, это интересная часть, а не бит «Не использовать переменные».
Как следствие такого образа мыслей, программы FP обычно описывают то, что произойдет, вместо конкретного механизма того, как это произойдет - это мощный инструмент, потому что, если мы можем четко указать, что означает «Выбрать», «Где» и «Агрегировать», мы могут свободно менять свои реализации, как мы это делаем с AsParallel (), и внезапно наше однопоточное приложение масштабируется до n ядер.
источник
Нет, потому что процедурный код может иметь побочные эффекты. Например, он может сохранять состояние между вызовами.
Тем не менее, можно написать код, удовлетворяющий этому ограничению, на языках, считающихся процедурными. И также можно написать код, который нарушает это ограничение на некоторых языках, считающихся функциональными.
источник
Я не согласен с ответом WReach. Давайте немного разберем его ответ, чтобы увидеть, откуда взялось разногласие.
Во-первых, его код:
и
Первое, что следует отметить, это то, что он объединяет:
программирование и отсутствие возможности для программирования в итеративном стиле иметь более явный поток управления, чем в типичном функциональном стиле.
Давайте быстро поговорим об этом.
Стиль, ориентированный на выражения, - это стиль, в котором вещи, насколько это возможно, оценивают вещи. Хотя функциональные языки славятся своей любовью к выражениям, на самом деле возможно иметь функциональный язык без составных выражений. Я собираюсь придумать такой, где нет выражений, только утверждения.
Это в значительной степени то же самое, что было указано ранее, за исключением того, что функции связаны исключительно цепочками операторов и привязок.
Стиль программирования, ориентированный на итератор, может быть принят Python. Давайте использовать чисто итеративный стиль, ориентированный на итераторы:
Это не работает, потому что каждое предложение является итеративным процессом, и они связаны друг с другом явной паузой и возобновлением кадров стека. Синтаксис может быть частично вдохновлен функциональным языком, но он применяется к его полностью итеративному варианту.
Конечно, вы можете сжать это:
Императив сейчас не так уж и плох, а? :)
Последний пункт касался более явного потока управления. Давайте перепишем исходный код, чтобы использовать это:
Используя итераторы, вы можете:
Так что это точка функционального языка , если разница между:
Главной отличительной чертой функционального языка программирования является то, что он устраняет мутации как часть типичной модели программирования. Люди часто понимают, что это означает, что в функциональном языке программирования нет операторов или выражений, но это упрощения. Функциональный язык заменяет явное вычисление объявлением поведения, которое затем сокращается.
Ограничение себя этим подмножеством функциональных возможностей дает вам больше гарантий относительно поведения ваших программ, и это позволяет вам создавать их более свободно.
Когда у вас есть функциональный язык, создание новых функций, как правило, так же просто, как создание тесно связанных функций.
Это непросто или, возможно, даже невозможно, если вы явно не контролировали глобальные зависимости функции. Лучшая особенность функционального программирования состоит в том, что вы можете последовательно создавать более общие абстракции и верить, что их можно объединить в единое целое.
источник
apply
не совсем такая же операция, какfold
илиreduce
, хотя я согласен с прекрасной способностью иметь очень общие алгоритмы.apply
значенииfold
илиreduce
, но мне кажется, что оно должно быть в этом контексте, чтобы возвращать логическое значение.В процедурной парадигме (лучше сказать «структурированное программирование»?) У вас есть общая изменяемая память и инструкции, которые читают / записывают ее в некоторой последовательности (одну за другой).
В функциональной парадигме у вас есть переменные и функции (в математическом смысле: переменные не меняются с течением времени, функции могут вычислять что-то только на основе своих входных данных).
(Это слишком упрощенно, например, FPL обычно имеют средства для работы с изменяемой памятью, тогда как процедурные языки часто могут поддерживать процедуры более высокого порядка, поэтому все не так однозначно; но это должно дать вам представление)
источник
Очаровательный Python: Функциональное программирование на Python от IBM Developerworks действительно помог мне понять разницу.
Особенно для тех, кто немного знаком с Python, примеры кода в этой статье, в которых противопоставляются разные действия функционально и процедурно, могут прояснить разницу между процедурным и функциональным программированием.
источник
В функциональном программировании, чтобы понять значение символа (переменной или имени функции), вам действительно нужно знать только 2 вещи - текущую область видимости и имя символа. Если у вас чисто функциональный язык с неизменяемостью, оба эти понятия являются «статическими» (извините за сильно перегруженное имя), то есть вы можете увидеть и текущую область видимости, и имя - просто взглянув на исходный код.
В процедурном программировании, если вы хотите ответить на вопрос, какова ценность,
x
вам также необходимо знать, как вы к этому пришли, одной лишь области видимости и имени недостаточно. И это то, что я считаю самой большой проблемой, потому что этот путь выполнения является свойством «времени выполнения» и может зависеть от стольких разных вещей, что большинство людей учатся просто отлаживать его, а не пытаться восстановить путь выполнения.источник
Недавно я подумал о различиях с точки зрения проблемы выражения . Описание Фила Вадлера часто цитируется, но принятый ответ на этот вопрос , вероятно, легче понять. По сути, кажется, что императивные языки склонны выбирать один подход к проблеме, тогда как функциональные языки склонны выбирать другой.
источник
Одно четкое различие между двумя парадигмами программирования - это состояние.
В функциональном программировании состояние избегается. Проще говоря, не будет переменной, которой присвоено значение.
Пример:
Однако в процедурном программировании используется состояние.
Пример:
источник