Функциональное программирование. Язык функционального программирования

Подобные языки к функциональным, использующим менее строгое понятие. Функция в математике не может изменить вызывающее её окружение и запомнить результаты своей работы, а только предоставляет результат вычисления функции. Программирование с использованием математического понятия функции вызывает некоторые трудности, поэтому функциональные языки, в той или иной степени предоставляют и императивные возможности, что ухудшает дизайн программы (например возможность безболезненных дальнейших изменений). Дополнительное отличие от императивных языков программирования заключается в декларативности описаний функций. Тексты программ на функциональных языках программирования описывают «как решить задачу», но не предписывают последовательность действий для решения. Первым, спроектированным функциональным языком стал Лисп . Вариант данного языка широко используется в системе автоматизированного проектирования AutoLISP

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

  • краткость и простота;
  • строгая типизация;
  • модульность;
  • функции - объекты вычисления;
  • отложенные (ленивые) вычисления.

Некоторые языки функционального программирования

  • Miranda (какое семейство?)
  • Ссылки

    • http://roman-dushkin.narod.ru/fp.html - Курс лекций по функциональному программированию , читаемый в МИФИ с 2001 года.

    Wikimedia Foundation . 2010 .

    Смотреть что такое "Функциональный язык программирования" в других словарях:

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

      функциональный язык - Язык программирования, в котором действия над данными выражаются в виде обращений к функциональным процедурам. [ГОСТ 19781 90] Тематики обеспеч. систем обраб. информ. программное EN functional language … Справочник технического переводчика

      Ruby Семантика: мультипарадигмальный Тип исполнения: интерпретатор Появился в: 1995 г. Автор(ы): Юкихиро Мацумото Последняя версия: 1.9.1 … Википедия

      Функциональный язык - 37. Функциональный язык Functional language Язык программирования, в котором действия над данными выражаются в виде обращений к функциональным процедурам Источник: ГОСТ 19781 90: Обеспечение систем обработки информации программное. Термины и… … Словарь-справочник терминов нормативно-технической документации

      Erlang Файл:Erlang logo.png Семантика: мультипарадигмальный: конкурентное, функциональное программирование Появился в: 1987 г. Автор(ы): Типизация данных: строгая, динамическая Основные реализации: E … Википедия

      Scheme Семантика: функциональный Тип исполнения: интерпретатор или компилятор Появился в: 1970 г. Автор(ы): Гай Стил и Джеральд Сассмен Типизация данных … Википедия

      У этого термина существуют и другие значения, см. Миранда. Miranda функциональный язык программирования, созданный в 1985 году Дэвидом Тёрнером в качестве стандартного функционального языка. Имеет строгую полиморфную систему типов,… … Википедия

      Hope функциональный язык программирования, разработанный в начале 1980 х годов; является предшественником языков Miranda и Haskell. В журнале Byte за август 1985 впервые опубликовано руководство по языку Hope. Пример программы вычисления… … Википедия

      У этого термина существуют и другие значения, см. SASL. SASL полностью функциональный язык программирования, разработанный Дэвидом Тёрнером в Сент Эндрюсском университете в 1972 году, на базе аппликативного подмножества ISWIM. В 1976 году… … Википедия

      У этого термина существуют и другие значения, см. Scala. Scala Класс языка: Мультипарадигмальный: функ … Википедия

    Книги

    • Программирование в Clojure. Практика применения Lisp в мире Java , Эмерик Ч., Карпер Б., Гранд К.. Почему многие выбирают Clojure? Это - функциональный язык программирования, не только позволяющий пользоваться Java-библиотеками, службами и другими ресурсами JVM, но и соперничающий с…

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

    1 Введение

    Программы на традиционных языках программирования, таких как Си, Паскаль, Java и т.п. состоят их последовательности модификаций значений некоторого набора переменных, который называется состоянием . Если не рассматривать операции ввода-вывода, а также не учитывать того факта, что программа может работать непрерывно (т.е. без остановок, как в случае серверных программ), можно сделать следующую абстракцию. До начала выполнения программы состояние имеет некоторое начальное значение σ0 , в котором представлены входные значения программы. После завершения программы состояние имеет новое значение σ0 , включающее в себя то, что можно рассматривать как «результат» работы программы. Во время исполнения каждая команда изменяет состояние; следовательно, состояние проходит через некоторую конечную последовательность значений:

    σ = σ0 → σ1 → σ2 → · · · → σn = σ0

    Состояние модифицируется с помощью команд присваивания , записываемых в виде v=E или v:=E, где v - переменная, а E - некоторое выражение. Эти команды следуют одна за другой; операторы, такие как if и while, позволяют изменить порядок выполнения этих команд в зависимости от текущего значения состояния. Такой стиль программирования называютимперативным илипроцедурным .

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

    1 Употребление термина «вычисление» не означает, что программа может оперировать только с числами; результатом вычисления могут оказаться строки, списки и вообще, любые допустимые в языке структуры данных.

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

    При сравнении функционального и императивного подхода к программированию можно заметить следующие свойства функциональных программ:

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

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

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

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

    Вместо циклов функциональные программы широко используют рекурсивные функции.

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

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

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

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

    Например, рассмотрим следующую программу на языке Haskell:

    factorial n = if n == 0 then 1 else n * factorial (n - 1)

    Практически сразу видно, что эта программа соответствует следующей частичной функции:

    f(n) = n! n ≥ 0

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

    int x = 1; while (n > 0)

    x = x * n; n = n - 1;

    Следует также сделать замечание относительно употребления термина «функция» в таких языках как Си, Java и т.п. В математическом смысле «функции» языка Си не являются функциями, поскольку:

    Их значение может зависеть не только от аргументов;

    Результатом их выполнения могут быть разнообразные побочные эффекты (например, изменение значений глобальных переменных)

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

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

    2 Основы лямбда-исчисления

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

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

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

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

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

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

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

    100% соответствие своей семантики с семантикой подразумеваемых конструкций лямбда-исчисления.

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

    Пусть f: R → R определяется следующим выражением:

    (x2 sin(1/x2 ),

    Тогда f0 (x) не интегрируема на интервале .

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

    Пусть x = 2 и y = 4. Тогда xx = y.

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

    где E - некоторое выражение, возможно, использующее переменную x.

    Пример. λx.x2 представляет собой функцию, возводящую свой аргумент в квадрат.

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

    Применение функции f к аргументу x мы будем обозначать как f x, т.е., в отличие от того, как это принято в математике, не будем использовать скобки2 . По причинам, которые станут ясны позднее, будем считать, что применение функции к аргументу ассоциативно влево, т.е. f x y

    2 Заметим, что и в математике такие выражения, как sin x записываются без скобок.

    означает (f(x))(y). В качестве сокращения для выражений вида λx.λy.E будем использовать запись λx y.E (аналогично для большего числа аргументов). Также будем считать, что «область действия» лямбда-выра- жения простирается вправо насколько возможно, т.е., например, λx.x y означает λx.(x y), а не (λx.x)y.

    На первый взгляд кажется, что нам необходимо ввести специальное обозначение для функций нескольких аргументов. Однако существует операция каррирования 3 , позволяющая записать такие функции в обычной лямбда-нотации. Идея заключается в том, чтобы использовать выражения вида λx y.x + y. Такое выражение можно рассматривать как функцию R → (R → R), т.е. если его применить к одному аргументу, результатом будет функция, которая затем принимает другой аргумент. Таким образом:

    (λx y.x + y) 1 2 = (λy.1 + y) 2 = 1 + 2.

    Переменные в лямбда-выражениях могут бытьсвободными исвязанными . В выражении вида x2 + x переменная x является свободной; его значение зависит от значения переменной x и в общем случае ее нельзя

    вать обозначение j, значение выражения не изменится.

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

    В лямбда исчислении выражения λx.E[x] и λy.E[y] считаются эквивалентными (это называется α-эквивалентностью, и процесс преобразования между такими парами называют α-преобразованием). Разумеется, необходимо наложить условие, что y не является свободной переменной в E[x].

    3 от фамилии известного логика Хаскелла Карри, в честь которого назван язык программирования Haskell

    3 Лямбда-исчисление как формальная система

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

    1. Переменные: обозначаются произвольными строками, составленными из букв и цифр.

    2. Константы: также обозначаются строками; отличие от переменных будем определять из контекста.

    3. Комбинации: , т.е. применения функции S к аргументу T ; и S и T могут быть произвольными лямбда-термами. Комбинация записывается как S T .

    4. Абстракции произвольного лямбда-терма S по переменной x, обозначаемые как λx.S.

    Таким образом, лямбда-терм определяется рекурсивно и его грамматику можно определить в виде следующей формы Бэкуса-Наура:

    Exp = Var| Const| Exp Exp| λ Var . Exp

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

    3.1 Свободные и связанные переменные

    В данном разделе мы формализуем данное ранее интуитивное представление о свободных и связанных переменных. Множество свободных

    переменных F V (S) лямбда-терма S можно определить рекурсивно следующим образом:

    Аналогично множество связанных переменных BV (S) определяется следующими формулами:

    BV (x) =

    BV (c) =

    BV (S T) = BV (S) BV (T)

    BV (λx.S) = BV (S) {x}

    Здесь предполагается, что c - некоторая константа.

    Пример. Для терма S = (λx y.x) (λx.z x) можно показать, что F V (S) = {z} и

    BV (S) = {x, y}.

    3.2 Подстановки

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

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

    3.3 Конверсия

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

    α-конверсия: λx.S −→ λy.S при условии, что y / F V (S).

    Например, λu.u v −→ λw.w u.

    β-конверсия: (λx.S) T −→ S.

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

    3.4 Равенство лямбда-термов

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

    под ней»:

    Следует отличать понятие равенства, определяемое этими формулами, от понятия синтаксической эквивалентности, которую мы будем обозначать специальным символом ≡. Например, λx.x 6≡λy.y, но λx.x = λy.y. Часто можно рассматривать синтаксическую эквивалентность термов с точностью до α-конверсий. Такую эквивалентность будем обозначать символом ≡α . Это отношение определяется так же, как равенство лямбда-термов, за тем исключением, что из всех конверсий допустимы только α-конверсии. Таким образом, λx.x ≡α λy.y.

    3.5 Экстенсиональность

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

    На практике отличие математической функции от понятия «функции» в императивном программировании заключается в том, что императивные функции могут опираться не только на аргументы, но и на состояние внешних по отношению к функции переменных, а также иметь побочные эффекты и менять состояние внешних переменных. Таким образом, в императивном программировании при вызове одной и той же функции с одинаковыми параметрами, но на разных этапах выполнения алгоритма, можно получить разные данные на выходе из-за влияния на функцию состояния переменных. А в функциональном языке при вызове функции с одними и теми же аргументами мы всегда получим одинаковый результат: выходные данные зависят только от входных. Это позволяет средам выполнения программ на функциональных языках кешировать результаты функций и вызывать их в порядке, не определяемом алгоритмом и распараллеливать их без каких-либо дополнительных действий со стороны программиста (что обеспечивают функции без побочных эффектов - чистые функции ).

    Энциклопедичный YouTube

      1 / 5

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

      Математика и константы / Введение в программирование, урок 4 (JavaScript ES6)

      Реактивное программирование и современные веб-интерфейсы

      Александр Чирцов о математике в физике

      Анна Андреева. Решение олимпиадных задач по математике

      Субтитры

    Языки функционального программирования

    Ещё не полностью функциональные изначальные версии и Лиспа , и APL внесли особый вклад в создание и развитие функционального программирования. Более поздние версии Lisp, такие как Scheme , а также различные варианты APL поддерживали все свойства и концепции функционального языка .

    Как правило, интерес к функциональным языкам программирования, особенно чисто функциональным, был скорее научный, нежели коммерческий. Однако, такие примечательные языки как Erlang , OCaml , Haskell , Scheme (после 1986) а также специфические (статистика), Wolfram (символьная математика), и (финансовый анализ), и XSLT (XML) находили применение в индустрии коммерческого программирования. Такие широко распространенные декларативные языки как SQL и Lex /Yacc содержат некоторые элементы функционального программирования, например, они остерегаются использовать переменные. Языки работы с электронными таблицами также можно рассматривать как функциональные, потому что в ячейках электронных таблиц задаётся массив функций, как правило зависящих лишь от других ячеек, а при желании смоделировать переменные приходится прибегать к возможностям императивного языка макросов.

    История

    Первым функциональным языком был Лисп , созданный Джоном Маккарти в период его работы в в конце пятидесятых и реализованный, первоначально, для IBM 700/7000 (англ.) русск. . В Лиспе впервые введено множество понятий функционального языка, хотя при этом в языке применяется не только парадигма функционального программирования . Дальнейшим развитием Лиспа стали такие языки как Scheme и Dylan .

    Концепции

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

    Функции высших порядков

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

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

    Чистые функции

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

    • Если результат чистой функции не используется, её вызов может быть удален без вреда для других выражений.
    • Результат вызова чистой функции может быть мемоизирован , то есть сохранен в таблице значений вместе с аргументами вызова. Если в дальнейшем функция вызывается с этими же аргументами, её результат может быть взят прямо из таблицы, не вычисляясь (иногда это называется принципом прозрачности ссылок). Мемоизация , ценой небольшого расхода памяти, позволяет существенно увеличить производительность и уменьшить порядок роста некоторых рекурсивных алгоритмов.
    • Если нет никакой зависимости по данным между двумя чистыми функциями, то порядок их вычисления можно поменять или распараллелить (говоря иначе вычисление чистых функций удовлетворяет принципам thread-safe)
    • Если весь язык не допускает побочных эффектов, то можно использовать любую политику вычисления. Это предоставляет свободу компилятору комбинировать и реорганизовывать вычисление выражений в программе (например, исключить древовидные структуры).

    Хотя большинство компиляторов императивных языков программирования распознают чистые функции и удаляют общие подвыражения для вызовов чистых функций, они не могут делать это всегда для предварительно скомпилированных библиотек, которые, как правило, не предоставляют эту информацию. Некоторые компиляторы, такие как gcc , в целях оптимизации предоставляют программисту ключевые слова для обозначения чистых функций . Fortran 95 позволяет обозначать функции как «pure» (чистые) .

    Рекурсия

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

    Подход к вычислению аргументов

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

    print (len ([ 2 + 1 , 3 * 2 , 1 / 0 , 5 - 4 ]))

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

    Как правило, нестрогий подход реализуется в виде редукции графа. Нестрогое вычисление используется по умолчанию в нескольких чисто функциональных языках, в том числе Miranda , Clean и Haskell . [ ]

    ФП в нефункциональных языках

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

    Стили программирования

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

    # императивный стиль target = # создать пустой список for item in source_list : # для каждого элемента исходного списка trans1 = G (item ) # применить функцию G() trans2 = F (trans1 ) # применить функцию F() target . append (trans2 ) # добавить преобразованный элемент в список

    Функциональная версия выглядит по-другому:

    # функциональный стиль # языки ФП часто имеют встроенную функцию compose() compose2 = lambda A , B : lambda x : A (B (x )) target = map (compose2 (F , G ), source_list )

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

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

    • Рефал (для этой категории, представленной единственным языком, нет общепринятого названия);
    • Аппликативные (Лисп , , Tcl , Rebol);
    • Комбинаторные (APL / / , FP /FL );
    • Бесточечные (чистые конкатенативные) (Joy , Cat , Factor , подмножество PostScript).

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

    Особенности

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

    Сильные стороны

    Повышение надёжности кода

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

    Удобство организации модульного тестирования

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

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

    Возможности оптимизации при компиляции

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

    Возможности параллелизма

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

    Недостатки

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

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

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

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

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

    На практике отличие математической функции от понятия «функции» в императивном программировании заключается в том, что императивные функции могут опираться не только на аргументы, но и на состояние внешних по отношению к функции переменных, а также иметь побочные эффектыи менять состояние внешних переменных. Таким образом, в императивном программировании при вызове одной и той же функции с одинаковыми параметрами, но на разных этапах выполнения алгоритма, можно получить разные данные на выходе из-за влияния на функцию состояния переменных. А в функциональном языке при вызове функции с одними и теми же аргументами мы всегда получим одинаковый результат: выходные данные зависят только от входных. Это позволяет средам выполнения программ на функциональных языках кешировать результаты функций и вызывать их в порядке, не определяемом алгоритмом. (см.ниже Чистые функции)



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

    Сильные стороны

    [править]Повышение надёжности кода

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

    [править]Удобство организации модульного тестирования

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

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

    [править]Возможности оптимизации при компиляции

    Традиционно упоминаемой положительной особенностью функционального программирования является то, что оно позволяет описывать программу в так называемом «декларативном» виде, когда жесткая последовательность выполнения многих операций, необходимых для вычисления результата, в явном виде не задаётся, а формируется автоматически в процессе вычисления функций.[источник не указан 1064 дня] Это обстоятельство, а также отсутствие состояний даёт возможность применять к функциональным программам достаточно сложные методы автоматической оптимизации.

    [править]Возможности параллелизма

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

    [править]Недостатки

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

    Для преодоления недостатков функциональных программ уже первые языки функционального программирования включали не только чисто функциональные средства, но и механизмы императивного программирования (присваивание, цикл, «неявный PROGN» были уже в LISPе). Использование таких средств позволяет решить некоторые практические проблемы, но означает отход от идей (и преимуществ) функционального программирования и написание императивных программ на функциональных языках.[источник не указан 1064 дня] В чистых функциональных языках эти проблемы решаются другими средствами, например, в языке Haskell ввод-вывод реализован при помощи монад - нетривиальной концепции, позаимствованной из теории категорий.

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

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

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

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

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

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

    К функциональным языкам программирования относят: Lisp, Miranda, Gofel, ML, Standard ML, Objective CAML, F#, Scala, Пифагор и др.

    Процедурные языки программирования

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

    Процедурные языки программирования: Ada, Basic, Си, КОБОЛ, Pascal, ПЛ/1, Рапира и др.

    Стековые языки программирования

    Стековый язык программирования − это язык программирования, в котором для передачи параметров используется машинная модель стека. Стековые языки программирования: Forth, PostScript, Java, C# и др. При использовании стека, в качестве основного канала передачи параметров между словами, элементы языка, естественным образом, образуют фразы (последовательное сцепление). Это свойство сближает данные языки с естественными языками.

    Аспектно-ориентированные языки программирования 5) Декларативные языки программирования 6) Динамические языки программирования 7) Учебные языки программирования 8) Языки описания интерфейсов 9) Языки прототипного программирования 10) Объектно-ориентированные языки программирования 11) Логические языки программирования 12) Сценарные языки программирования 13) Эзотерические языки программирования


    Стандартизация языков программирования. Парадигма программирования

    Концепция языка программирования неотрывно связана с его реализацией. Для того чтобы компиляция одной и той же программы различными компиляторами всегда давала одинаковый результат, разрабатываются стандарты языков программирования. Организации, занимающиеся вопросами стандартизации: Американский национальный институт стандартов ANSI, Институт инженеров по электротехнике и электронике IEEE, Организация международных стандартов ISO.



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

    Парадигмы программирования

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

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

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


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

    Программирование – процесс создания компьютерных программ. В более широком смысле: спектр деят-сти, связ-ый с созданием и поддержанием в раб. состоянии программ - ПО ЭВМ.

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

    Технология программир-я представляет собой набор технологических инструкций, включающих:

    · указание последоват-сти выполнения технологич-х операций;

    · перечисление условий, при кот-х выполняется та или иная операция;

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

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

    Важным этапом создания прогр-го продукта явл. тестирование и отладка.

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

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

    Существуют следующие методы тестирования ПС:

    1) Статическое тестирование – ручная проверка программы за столом.

    2) Детерминированное тестир-е – при разл-х комбинациях исх-х данных.

    3) Стохастическое – исх. данные выбир-ся произвольно, на выходе определяется качеств-е совпадение результатов или примерная оценка.


    Стили программирования.

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

    Существует несколько стилей программирования:

    1. Процедурное программирование – это программирование, при котором программа представляет собой последовательность операторов. Используется в языках высокого уровня Basic, Fortran и др.
    2. Функциональное программирование – это программирование, при котором программа представляет собой последовательность вызовов функций. Используется в языках Lisp и др.
    3. Логическоепрограммирование – это программирование, при котором программа представляет собой совокупность определения соотношений между объектами. Используется в языках Prolog и др.

    Объектно-ориентированноепрограммирование – это программирование, при котором основой программы является объект представляющий собой совокупность данных и правил их преобразования. Используется в языках Turbo-Pascal, C++ и др.



    Есть вопросы?

    Сообщить об опечатке

    Текст, который будет отправлен нашим редакторам: