Подлинное понимание разницы между процедурным и функциональным

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

вот первые два абзаца из Википедии о функциональное программирование:

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

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

в параграфе 2, где говорится

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

разве это не тот же самый случай для процедурного программирования?

что следует искать в процедурных vs функциональных, которые выделяются?

8 ответов


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

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

давайте рассмотрим аналогию с "обычных" значений. Мы можем взять два целых значения и объединить их с помощью + оператор для получения нового целого числа. Или мы можем умножить целое число на число с плавающей запятой, чтобы получить число с плавающей запятой.

в функциональном программировании, мы можем совместить 2 значения функции для произведения а новое значение функции с использованием операторов типа compose или лифт. Или мы можем объединить значение функции и значение данных для получения нового значения данных с помощью операторов, таких как карта или раза.

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

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

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

контраст

function allOdd(words) {
  var result = true;
  for (var i = 0; i < length(words); ++i) {
    var len = length(words[i]);
    if (!odd(len)) {
      result = false;
      break;
    }
  }
  return result;
}

Я буду считать, что этот пример понятен. Теперь, функциональный стиль:

function allOdd(words) {
  return apply(and, map(compose(odd, length), words));
}

работая изнутри наружу, это определение делает следующие вещи:

  1. compose(odd, length) сочетает в себе odd и length функции для создания новой функции, которая определяет, является ли длина строки нечетная.
  2. map(..., words) вызывает эту новую функцию для каждого элемента в words, в конечном итоге возвращает новый список логических значений, каждый из которых указывает, имеет ли соответствующее слово нечетное число символов.
  3. apply(and, ...) применяет оператор " и " к результирующему списку, и - ing все булевы вместе, чтобы получить окончательный результат.

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

в Примере также показаны типичные относительные размеры процедурного и функционального кода. Кроме того, он демонстрирует, что эксплуатационные характеристики процессуальный кодекс может быть легче увидеть, чем функциональный код. Рассмотрим: вычисляют ли функции длины всех слов в списке или каждый останавливается сразу после нахождения первого четного слова длины? С другой стороны, функциональный код позволяет высококачественной реализации выполнять довольно серьезную оптимизацию, поскольку он в первую очередь выражает намерение, а не явный алгоритм.

Более Дальнеишее Чтение

этот вопрос часто всплывает... см., например:

Джон Бэкус премия Тьюринга лекция излагает мотивации для функционального программирования в большой деталь:

можно ли освободить Программирование от стиля фон Неймана?

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


дополнение

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

  • query (например, list comprehensions, language-integrated query)
  • поток данных (например, неявная итерация, массовые операции)
  • объектно-ориентированные (например, инкапсулированные данные и методы)
  • ориентированный на язык (например, синтаксис приложения, макросы)

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

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


реальная разница между функциональным и императивным программированием-это мышление-императивные программисты думают о переменных и блоках памяти, в то время как функциональные программисты думают: "как я могу transform мои входные данные в мои выходные данные" - ваша "программа" - это конвейер и набор преобразований на данные чтобы взять его от входа к выходу. Это интересная часть IMO, а не " ты не должен использовать переменные" немного.

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


     Isn't that the same exact case for procedural programming?

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

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


Я не согласен с ответом WReach по. Давайте немного разберем его ответ, чтобы увидеть, откуда берутся разногласия.

во-первых, его код:

function allOdd(words) {
  var result = true;
  for (var i = 0; i < length(words); ++i) {
    var len = length(words[i]);
    if (!odd(len)) {
      result = false;
      break;
    }
  }
  return result;
}

и

function allOdd(words) {
  return apply(and, map(compose(odd, length), words));
}

первое, что следует отметить, что он объединяет:

  • функциональное
  • выражение ориентированного и
  • итератор ориентированных

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

давайте быстро поговорим об этих.

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

lengths: map words length
each_odd: map lengths odd
all_odd: reduce each_odd and

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

итератор-ориентированный стиль программирования может быть взят Python. Давайте использовать чисто итеративный, итератор-ориентированный стиль:

def all_odd(words):
    lengths = (len(word) for word in words)
    each_odd = (odd(length) for length in lengths)
    return all(each_odd)

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

конечно, вы можете сжать это:

def all_odd(words):
    return all(odd(len(word)) for word in words)

необходимо не так уж плохо, а? :)

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

function allOdd(words) {
    for (var i = 0; i < length(words); ++i) {
        if (!odd(length(words[i]))) {
            return false;
        }
    }
    return true;
}

С помощью итераторов можно:

function allOdd(words) {
    for (word : words) { if (!odd(length(word))) { return false; } }
    return true;
}

так что is точка функционального языка, если разница между:

return all(odd(len(word)) for word in words)
return apply(and, map(compose(odd, length), words))
for (word : words) { if (!odd(length(word))) { return false; } }
return true;


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

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

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

all = partial(apply, and)

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


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

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

(Это упрощено, например, FPLs обычно имеют средства для работы с изменяемой памятью в то время как процедурные языки часто могут поддерживать процедуры более высокого порядка, поэтому все не так четко; но это должно дать вам представление)


на очаровательный Python: функциональное программирование в Python С IBM Developerworks действительно помог мне понять разницу.

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


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

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


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