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

На втором курсе университета нас «учили» Haskell, я почти ничего не знаю о нем и тем более о функциональном программировании.

Что такое функциональное программирование, почему и / xor, где я хотел бы использовать его вместо нефункционального программирования, и правильно ли я думаю, что C - нефункциональный язык программирования?


person Teifion    schedule 23.08.2008    source источник


Ответы (8)


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

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

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

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

person Kyle Cronin    schedule 23.08.2008
comment
Основная причина этого в том, что последовательные вызовы функции будут давать один и тот же результат Why do we want successive calls to a function to yield the same result. What is the problem with the way things are in C? I never understood this. - person Lazer; 23.05.2010
comment
@Lazer Это нежелательно по нескольким причинам. Во-первых, если функция возвращает тот же результат, вы можете кэшировать этот результат и вернуть его при повторном вызове функции. Во-вторых, если функция не возвращает тот же результат, это означает, что она полагается на какое-то внешнее состояние программы, и поэтому функцию нельзя легко предварительно вычислить или распараллелить. - person Kyle Cronin; 23.05.2010
comment
Также это огромное преимущество при анализе и отладке кода. Побочные эффекты - это просто неявный ввод функции, скрытый от программиста. - person Magnus Kronqvist; 24.05.2011
comment
@MagnusKronqvist, если они изменяемые, они не такие же, как неявные входы, потому что в них можно записывать. Входные аргументы обычно не могут быть записаны (если они не относятся к ссылочному типу). Важность различия заключается в том, что доступ к изменяемому внешнему состоянию делает функцию императивно, а не декларативно. - person Shelby Moore III; 08.12.2011
comment
Вы можете писать функциональный код на любом языке, который поддерживает первоклассные функции - я бы добавил, что 1) наличие поддержки классов типов на языке избавляет вас от большого количества повторений, и 2) Java также может представлять функции первого класса (с классом с одним методом объекты, см. Функцию Guava из F FunctionalJava), но синтаксис слишком подробный, чтобы его можно было использовать. - person ron; 21.05.2012

Что такое функциональное программирование

Сегодня широко используются два разных определения «функционального программирования»:

Старое определение (происходящее из Лиспа) заключается в том, что функциональное программирование - это программирование с использованием функций первого класса, то есть где функции обрабатываются как любое другое значение, поэтому вы можете передавать функции в качестве аргументов другим функциям, а функция может возвращать функции среди своих возвращаемых значений. Это приводит к использованию функций высшего порядка, таких как map и reduce (вы, возможно, слышали о mapReduce как об отдельной операции, активно используемой Google, и, что неудивительно, она является близкой родственницей!). Типы .NET System.Func и System.Action делают функции высшего порядка доступными в C #. Хотя каррирование в C # нецелесообразно, функции, которые принимают другие функции в качестве аргументов, являются общими, например функция Parallel.For.

Младшее определение (популяризированное Haskell) заключается в том, что функциональное программирование также связано с минимизацией и контролем побочных эффектов, включая мутации, то есть написание программ, которые решают проблемы путем составления выражений. Это чаще называют «чисто функциональным программированием». Это стало возможным благодаря совершенно разным подходам к структурам данных, которые называются «чисто функциональными структурами данных». Одна из проблем заключается в том, что перевод традиционных императивных алгоритмов на использование чисто функциональных структур данных обычно снижает производительность в 10 раз. Haskell - единственный сохранившийся чисто функциональный язык программирования, но его концепции вошли в массовое программирование с помощью таких библиотек, как Linq на .NET.

где бы я хотел использовать это вместо нефункционального программирования

Повсюду. Лямбды в C # теперь продемонстрировали основные преимущества. В C ++ 11 есть лямбды. Сейчас нет оправдания тому, чтобы не использовать функции высшего порядка. Если вы можете использовать такой язык, как F #, вы также выиграете от вывода типов, автоматического обобщения, каррирования и частичного применения (а также от множества других языковых функций!).

Правильно ли я считаю, что C - нефункциональный язык программирования?

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

person J D    schedule 27.01.2013
comment
Не могли бы вы подробнее рассказать об этой строке или поделиться примером: одна проблема заключается в том, что перевод традиционных императивных алгоритмов на использование чисто функциональных структур данных обычно в 10 раз ухудшает производительность. - person Jaguar; 01.11.2015
comment
Почти все традиционные алгоритмы (например, быстрая сортировка, LZW, разложение LU, MST Прима) почти всегда по своей природе императивны. В частности, они никогда не выиграют от постоянства: они всегда работают только с последней версией коллекции и никогда не используют повторно старые версии. Это делает их идеальными для реализации с использованием традиционных изменяемых коллекций, но это означает, что если вы реализуете их с использованием чисто функциональных коллекций, вы платите цену за постоянство (т.е. медленную скорость), но не получаете никаких преимуществ. - person J D; 26.01.2020
comment
Вместо этого вы должны более внимательно искать экзотические алгоритмы, которые решают ту же проблему и действительно выигрывают от настойчивости, такие как сортировка слиянием и MST Борувки. - person J D; 26.01.2020

Возможно, стоит недавно прочитать эту статью о F # 101 в CoDe Mag. опубликовал.

Кроме того, Дастин Кэмпбелл ведет отличный блог, в котором он опубликовал множество статей о своих приключениях по освоению F #. .

Надеюсь, они вам пригодятся :)

РЕДАКТИРОВАТЬ:

Кроме того, просто чтобы добавить, мое понимание функционального программирования таково, что все - это функция или параметры функции, а не экземпляры / объекты с отслеживанием состояния .. Но я могу ошибаться F # - это то, что я умираю чтобы попасть, но просто нет времени! :)

person Rob Cooper    schedule 23.08.2008

В примере кода Джона Статистика не показано функциональное программирование, потому что, когда вы выполняете функциональное программирование, ключевым моментом является то, что код НЕ выполняет НАЗНАЧЕНИЙ (record = thingConstructor(t) - это присвоение) и НЕ имеет ПОБОЧНЫХ ЭФФЕКТОВ (localMap.put(record) - это оператор со стороной эффект). В результате этих двух ограничений все, что делает функция, полностью фиксируется ее аргументами и возвращаемым значением. Переписать код статистика так, как он должен выглядеть, если вы хотите эмулировать функциональный язык с использованием C ++:

RT getOrCreate(const T thing, 
                  const Function<RT<T>> thingConstructor, 
                  const Map<T,RT<T>> localMap) {
    return localMap.contains(t) ?
        localMap.get(t) :
        localMap.put(t,thingConstructor(t));
}

В результате правила отсутствия побочных эффектов каждый оператор является частью возвращаемого значения (следовательно, return идет первым), и каждый оператор является выражением. В языках, требующих функционального программирования, подразумевается ключевое слово return, а оператор if ведет себя как оператор ?: в C ++.

Кроме того, все неизменяемо, поэтому localMap.put должен создать новую копию localMap и вернуть ее вместо изменения исходного localMap, как это сделала бы обычная программа на C ++ или Java. . В зависимости от структуры localMap копия может повторно использовать указатели в оригинале, уменьшая объем данных, которые необходимо скопировать.

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

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

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

Функциональные языки также имеют очень большие среды выполнения. Haskell - исключение (исполняемые файлы GHC почти такие же маленькие, как программы C, как во время компиляции, так и во время выполнения), но программы SML, Common Lisp и Scheme всегда требуют тонны памяти.

person Community    schedule 18.02.2009
comment
Я не согласен с тем, что функциональные языки могут (и, как и Erlang, иногда допускают) разрешать одиночные назначения, которые не имеют побочных эффектов. Тем не менее, ваше мнение о побочных эффектах хорошо понимается. Однако я не собираюсь менять свой код, поскольку считаю, что демонстрация преимуществ функциональных возможностей гибридного языка более важна, чем строгая чистота. - person John with waffle; 24.02.2010
comment
Подавляющее большинство функционального программирования касается использования первоклассных функций, а не запрета побочных эффектов. Мои измерения показывают, что ваши утверждения о размерах сред выполнения неверны: GHC производит двоичные файлы, вдвое превышающие размер OCaml и Standard ML (с использованием MLton) и в 20 раз превышающие размер HLVM. - person J D; 22.12.2010
comment
Не объединяйте FP с отсутствием побочных эффектов, то есть с DP по сравнению с IP. Это чистый FP. - person Shelby Moore III; 08.12.2011

Да, вы правы, полагая, что C - нефункциональный язык. C - процедурный язык.

person robintw    schedule 23.08.2008

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

SomeKindOfRecord<T> getOrCreate(T thing) { 
    if(localMap.contains(thing)) { return localMap.get(thing); }
    SomeKindOfRecord<T> record = new SomeKindOfRecord<T>(thing);
    localMap = localMap.put(thing, record);
    return record; 
}

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

RT<T> getOrCreate(T thing, 
                  Function<RT<T>> thingConstructor, 
                  Map<T,RT<T>> localMap) {
    if(localMap.contains(thing)) { return localMap.get(thing); }
    RT<T> record = thingConstructor(thing);
    localMap = localMap.put(thing,record);
    return record; 
}

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

getOrCreate = myLib.getOrCreate(*,
                                SomeKindOfRecord<T>.constructor(<T>), 
                                localMap);

(где * - это своего рода обозначение "оставить этот параметр открытым", что является своего рода каррированием)

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

person John with waffle    schedule 23.08.2008

Если вы ищете хороший текст на F #

Expert F # написан в соавторстве с Доном Саймом. Создатель F #. Он специально работал над дженериками в .NET, чтобы создать F #.

F # создан по образцу OCaml, поэтому любой текст OCaml также поможет вам изучить F #.

person Brian Leahy    schedule 23.08.2008
comment
Неужели Дон действительно только что сделал дженерики для F #? Я нахожу это подозрительно удивительным! - person J D; 22.12.2010

Я считаю полезным Что такое функциональное программирование?

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

Предпочитать явный when параметр

public Program getProgramAt(TVGuide guide, int channel, Date when) {
  Schedule schedule = guide.getSchedule(channel);

  Program program = schedule.programAt(when);

  return program;
}

над

public Program getCurrentProgram(TVGuide guide, int channel) {
  Schedule schedule = guide.getSchedule(channel);

  Program current = schedule.programAt(new Date());

  return current;
}

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

person onmyway133    schedule 07.01.2016