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

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


  • 239 Views
  • Uploaded on

Функциональное программирование. Лекция 2 (13) Функционалы и их разновидности. Содержание. Функционалы общие определения виды и примеры Лямбда-выражения Особые виды функционалов композиции фильтры редукции. Функционалы. Определение, примеры. Понятие отображения.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' Функциональное программирование' - mort


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
Функциональное программирование

Лекция 2 (13)

Функционалы и их разновидности


Содержание программирование

  • Функционалы

    • общие определения

    • виды и примеры

  • Лямбда-выражения

  • Особые виды функционалов

    • композиции

    • фильтры

    • редукции


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

Определение, примеры


Понятие отображения программирование

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

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

  • При определении отображений, прежде всего, д.б. ясны следующие вопросы:

    • что представляет собой отображающая функция;

    • как организовано данное, представляющее отображаемое множество;

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

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

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

    • где размещается множество полученных результатов;

    • чем отличаются нужные результаты от полученных попутно;

    • как строится итоговое данное из отобранных результатов.


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

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

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

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

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

  • Аргумент, значением которого является функция, называют функциональным аргументом, а функцию, имеющую функциональный аргумент - функционалом.

  • Различие между понятиями "данные" и " функция", определяются не на основе их структуры, а в зависимости от использования:

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

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


Пример программирование: обычные похожие функции

  • Построить список из <голов> элементов списка

    (defun 1st (xl)

    (cond ; "головы" элементов = CAR пока список не пуст

    (xl (cons (caar xl); выбираем CAR от его головы

    (1st (cdr xl)) ; и переходим к остальным,

    ) ) ) ) ; собирая результаты в список

    > (1st`((один два)(one two)(1 2)) )

    (один one 1)

  • Пример: Выяснить длины элементов списка

    (defun lens (xl) ; Длины элементов

    (cond ; Пока список не пуст

    (xl (cons (length (car xl))

    ; вычисляем длину его головы

    (lens (cdr xl)); и переходим к остальным,

    ) ) ) ) ; собирая результаты в список

    > (lens `((1 2)()(a b c d)(1(a b c d)3)))

    (2 0 4 3)


Сводим обе функции к одной – функционалу

  • Внешние отличия в записи этих трех функций малосущественны, что позволяет ввести более общую функцию mapcar, в определении которой имена <car> и <length> могут быть заданы как значения параметра fn:

    (defun mapcar(fn xl)

    (cond ; Поэлементное преобразование XL с помощью функции FN

    (xl (cons(funcall fn (car xl)) ; применяем FN как функцию голове XL

    (mapcar fn (cdr xl))

    ) ) ) ) ; и переходим к остальным, собирая результаты в список

  • Эффект функций 1st и lens можно получить выражениями:

    (mapcar `car xl) ; "головы" элементов = CAR

    (mapcar `length xl) ; Длины элементов

    > (mapcar `car `((один два)(one two)(1 2)))

    (один one 1)

    > (mapcar `length `((1 2)()(a b c d)(1(a b c d)3)) )

    (2 0 4 3)

  • Оба примера можно решить с помощью таких определяющих выражений:

    (defun 1st(xl) (mapcar `car xl)) ; "головы" элементов = CAR

    (defun lens(xl) (mapcar `length xl)) ; Длины элементов

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


Покомпонентная обработка: пример обработки двух списков

  • Задача: Построить ассоциативный список, т.е. список пар из имен и соответствующих им значений, по заданным спискам имен и их значений:

    (DEFUN pairl (al vl) ; Ассоциативный список

    (COND ; Пока al не пуст,

    (al (CONS (CONS (CAR al) (CAR vl))

    ; пары из <голов>.

    (pairl (CDR al) (CDR vl))

    ; Если vl исчерпается,

    ; то CDR будет давать NIL

    ) ) ) )

    > (pair '(один два two three) '(1 2 два три))

    ((один . 1)(два . 2)(two . два)(three . три))


Функционал для покомпонентной обработки

  • Теперь попробуем определить по аналогии универсальную функцию покомпонентной обработки двух списков с помощью заданной функции FN:

    (DEFUN map-comp (fn al vl) ; fn покомпонентно применитьк al и vl

    (COND

    (al (CONS (FUNCALL fn (CAR al) (CAR vl))

    ; Вызов данного fn как функции

    (map-comp (CDR al) (CDR vl))

    ) ) ) )

  • Теперь покомпонентные действия над векторами, представленными с помощью списков, полностью в наших руках. Достаточно уяснить, что надо делать с элементами списка, остальное довершит функционал MAP-COMP, отображающий этот список в список результатов заданной функции. Вот списки и сумм, и произведений, и пар, и результатов проверки на совпадение:

    >(map-comp #'+'(1 2 3) '(4 6 9)) ; Суммы

    (5 8 12)

    > (map-comp #'*'(1 2 3) '(4 6 9)) ; Произведения

    (4 12 27)

    >(map-comp #'CONS'(1 2 3) '(4 6 9)) ; Пары

    ((1 . 4) (2 . 6) (3 . 9))

    >(map-comp #'EQ'(4 2 3) '(4 6 9)) ; Сравнения

    (T NIL NIL)


Mapcar
Пример применения функционала обработки MAPCAR к нескольким аргументам

  • MAPCAR может обрабатывать больше списков, если в функциональном аргументе несколько аргументов.

    (defun addlist (l1 l2) (mapcar '+ l1 L2))

    > (addlist '( 1 2 3) '(1 2 3))

    (2 4 6)

    ~ (list (+ 1 1) (+ 2 2) (+ 3 3))

  • Если списки разной длины, то длина результата будет равна длине наименьшего.


Виды функционалов обработки

Отображающие, применяющие…


Применяющие функционалы обработки

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

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

  • Общий формат:

    (<имя_функционала><применяемая_функция><аргумент>)


Apply
Функционал обработки APPLY

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

  • Пример : Предположим мы хотим объединить в один список несколько вложенных списков, т.е. из

    ((a b c) (d e f) (k l)) получить (a b c d e f k l)

  • Для этой задачи используем функцию apply, которая имеет два аргумента: имя функции и список, и применяет названную функцию к элементам списка, как к аргументам функции:

    > (apply 'append '((a)(b)(c)))

    (A B C)


Apply1
Примеры применения обработки APPLY

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

    Пример: Определим функцию, которая рассчитывает среднее списка чисел

    (defun list-mean (x) (/ (apply `+ x) (length x)))

    > (list-mean `(1 2 3 4))

    2.5

  • Часто apply используют вместе c марсаr.

    Пример: найти общее число элемент в списках

    (defun countall (lis)

    (apply `+ (mapcar ` length lis))

    )

    > (countall `((a b c) (d e f) (k l)))

    8

  • Можно определить более сложную функцию countatom, которая считает элементы-атомы в любом списке.

    (defun countatom (lis)

    (cond

    ((null lis) 0)

    ((atom lis) 1)

    (t (apply `+ (mapcar `countatom lis)))))

    > (countatom `(a (a (b) c) (d) e (f g)))

    8


Funcall
Функционал обработки FUNCALL

  • Применяющий функционал FUNCALL аналогичен APPLY, но аргументы он принимает, не в списке, а по отдельности:

    (funcall fn x1 x2 ... xN) ~ (fn x1 x2 ... xN)

    • здесь fn - функция с n aргументами.

      > (funcall '+ 1 2)

      3

      > (+ 1 2)

      3

      > (funcall (car '(+ - / *)) 1 2)

      3


Funcall1
Пример использования обработки funcall

Рассмотрим использование funcallдля построения функции map2, которая действует аналогично mapcar, но берет в качестве аргументов два элемента из списка, а не один.

Эта функция имеет вид:

(defun map2 (f2 lst)

(cond

((null lst) nil)

(t (cons

(funcall f2 (car lst) (cadrlst))

; выполняем конкатенацию результаты вызова ф-ии

; f2 для головы и 2го элемента

(map2 f2 (cddrlst)))

; с результатом рекурсивного вызова исходной ф-ии

; для «укороченного» хвоста

)

)

)

>(map2 'list '(A Christie V Nabokov K Vonnegut))

((A Christie) (V Nabokov) (K Vonnegut))


Отображающие функционалы обработки

  • Важный класс функционалов используемых в лиспе - отображающие функционалы (МАР функционалы)- функции, которые некоторым образом отображают (map) заданный список в новый список. MAPCAR один из основных отображающих функционалов. Он имеет два аргумента: 1) функция, 2) список:

    (MAPCAR f '(x1 x2 x3 ... xN))

  • Когда MAPCAR выполняется , функция определенная первым аргументом применяется к каждому элементу, списка, определенному вторым аргументом и результат помещает (отображает) в новый список.

    > (mapcar 'add1 '( 1 2 3))

    (2 3 4)

  • (MAPCAR f '(x1 x2 x3 ... xN))эквивалентнозаписи:

    (list (f 'x1) (f 'x2) .... (f 'xN))

  • Можно использовать в функциях

    (defun list-add1 (lis) (mapcar 'add1 lis))

    > (list-add1 '(1 2 3))

    (2 3 4)

  • В качестве аргумента для MAPCAR может быть использовано значение символа

    > (setq x '(a b (d)))

    (setq y 'atom)

    > (mapcar y x)

    (t t nil)


Отображающие функционалы обработки

  • Отображающий функционал можно написать самим, а можно и воспользоваться одним из встроенных. Согласно стандарту, в базовую реализацию языка Лисп обычно включены функционалы: map, mapcar, maplist, mapcan, mapcon, mapc, mapl. Каждый из них покомпонентно обработает любой набор списков. Отличаются они схемами выбора аргументов для отображающей функции, характером воздействия на исходные данные и оформлением результатов, передаваемых объемлющим формулам. Рассмотрим их по отдельности, с примерами вызова.

  • Map(map result-type function sequences ... )

    Функция function вызывается на всех первых элементах последовательностей, затем на всех вторых и т.д. Из полученных результатов function формируется результирующая последовательность, строение которой задается параметром result-type с допустимыми значениями cons, list, array, string, NIL.

    > (map `list `+ `(1 2 3) `(4 5 6))

    (5 7 9) 

  • Mapcar( mapcar function list ... )

    Функция function применяется к первым элементам списков, затем ко вторым и т.д. Другими словами, function применяется к <головам> методично сокращающихся списков, и результаты применения собираются в результирующий список.

    > (mapcar `list `(1 2 3) `(4 5 6))

    ((1 4)(2 5)(3 6)) 


Отображающие функционалы обработки

  • Maplist ( maplist function list ... )

    Функционал аналогичен mapcar, но function применяется к <<хвостам>> списков list, начиная с полного списка.

    > (maplist `list `(1 2 3) `(4 5 6))

    (((1 2 3) (4 5 6)) ((2 3) (5 6)) ((3) (6)))

  • Mapc и Mapl

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

    > (mapc `list `(1 2 3) `(4 5 6)) > (mapl `list `(1 2 3) `(4 5 6))

    (1 2 3) (1 2 3)

  • Mapcan и Mapcon

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

    > (mapcan `list `(1 2 3 4)) > (mapcon `list `(1 2 3 4))

    (1 2 3 4) ((1 2 3 4) (2 3 4) (3 4) (4))

  • Map-into

    Функционал отображает результат в конкретную последовательность.

    > (setq a (list 1 2 3 4) b (list 10 10 10 10)) > (map-into a `+ a b)

    (10 10 10 10) (11 12 13 14)


Лямбда-выражения обработки

Безымянные функции


Зачем нужны лямбда-выражения, безымянные функции

  • Определения в описанных примерах не вполне удобны по следующим причинам:

    • в определениях целевых функций встречаются имена специально определенных вспомогательных функций;

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

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

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

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

    (LAMBDA (x1 ... xK) expr)

  • Имени такая функций не имеет, поэтому м.б. применена лишь непосредственно. DEFUN использует данный конструктор, но требует дать функциям имена.

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


Лямбда-выражения безымянные функции

  • Структура МАР функций ограничивает формы отображаемых функций. Так, если мы желаем получить список с элементами x * x + 1, илиx + 1 мы должны определить функции

    (defun f1 (x) (+ 1 (* xx)))

    > (mapcar 'f1 '(1 2 3))

    (2 5 10)

    (defunf2 (x) (+ 1 x))

    > (mapcar 'f2 '(1 2 3))

    (2 3 4)

  • Таким образом определяется специальная функция, которая используется только в MAPCAR.Более эффективно в этом случае использовать, т.н. лямбда выражения:

    > (mapcar '(lambda (x) (+ 1 (* xx))) '(1 2 3))

    > (mapcar '(lambda (x) (+ 1 x)) '(1 2 3))

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

    (Лямбда-выражения определяют функцию не имеющую имени)

  • Общая формазаписи:

    (lambda (параметры) <тело функции>)

    Рассмотрим подробнее…


Пример использования безымянные функциилямбда-выражений

  • Пусть дана вспомогательная функция sqw, возводящая числа в квадрат

    (defun sqw (x)(* x x)) ; Возведение числа в квадрат

    > (sqw 3)

    9

  • Построить список квадратов чисел, используя функцию sqw:

    (defun sqware (xl)

    ; Возведение списка чисел в квадрат

    (cond ; Пока аргумент не пуст,

    (xl (cons (sqw (car xl))

    ; применяем sqw к его голове

    (sqware(cdr xl))

    ; и переходим к остальным,

    ) ) ) ) ; собирая результаты в список

    (sqware`(1 2 5 7))

    ; (1 4 25 49 )

  • Можно использовать mapcar:

    (defun sqware (xl) (mapcar `sqw xl))

  • Но является ли это решение самым компактным, и эффективным?


Используем лямбда-выражение безымянные функции

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

    (defun sqware (xl)

    (cond

    (xl (cons (* (car xl) (car xl) )

    (sqware (cdr xl))

    ) ) ) )

  • Определение функции sqware – вычисляющей квадраты элементов числового списка, без использования имен и вспомогательных функций (оцените краткость!):

    (defun sqware (xl)

    (mapcar `(lambda (x) (* x x)) xl)

    )


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

Лишние сложности или упрощение вычислений?


Композиции функционалов фильтры и редукции

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

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

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

    • и серии, и последовательности представляются списками

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

    (defun mapf (fl el)

    (cond ; Пока первый аргумент не пуст,

    (fl (cons (funcall (car fl) el)

    ; применяем очередную функцию

    ; ко второму аргументу

    (mapf (cdr fl) el)

    ; и переходим к остальным функциям,

    ) ) ) ) ; собирая их результаты в общий список

    >(mapf `(length car cdr) `(a b c d))

    4 a (b c d))


Фильтры фильтры и редукции

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

  • Пример: Построить список голов непустых списков:

    ; временно голова размещается в список,

    ; чтобы потом списки сцепить

    (defun heads (xl)

    (map-ap

    `(lambda (x)

    (cond

    (x

    (cons (car x) NIL)

    ) ) )

    xl

    ) )

    > (heads `((1 2) () (3 4) () (5 6)) )

    (1 3 5)


Редукция фильтры и редукции

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

  • Редукция заключается в сведении множества элементов к одному элементу, в вычислении которого задействованы все элементы множества.

  • Пример: Подсчитать сумму

    элементов заданного списка.

(*) Перестроим такое определение, чтобы вместо <+>

можно было использовать любую бинарную функцию:

(defun red-el (fn xl)

(cond

((null xl)

0

)

(xl

(funcall fn

(car xl)

(red-el fn (cdr xl))

)))

)

> (red-el `+ `(1 2 3 4) )

10

(defun sum-el (xl)

(cond

((null xl)

0

)

(xl

(+

(car xl)

(sum-el (cdr xl) )

)))

)

>(sum-el `(1 2 3 4) )

10


Выводы по применимости функционалов

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

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

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

  • функционалы обеспечивают гибкость отображений;

  • определение функции может совсем не зависеть от конкретных имен;

  • с помощью функционалов можно управлять выбором формы результатов;

  • параметром функционала может быть любая функция, преобразующая элементы структуры;

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

  • встроенные в Лисп базовые функционалы приспособлены к покомпонентной обработке произвольного числа параметров;

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


Литература функционалов

  • Л.В. Городняя. Основы функционального программирования – ИНТУИТ

    [ http://www.intuit.ru/department/pl/funcpl/]

  • Морозов М.Н. Функциональное программирование. Курс лекций - 1999-2001 г.

    [ http://www.mari-el.ru/mmlab/home/lisp/title.htm ]


ad