30
Ю У р Г У М ехМ ат С П Функциональное программирование Лекция 2 (13) Функционалы и их разновидности

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

  • Upload
    mort

  • View
    58

  • Download
    0

Embed Size (px)

DESCRIPTION

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

Citation preview

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

Ю У р Г У

МехМат

С П

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

Лекция 2 (13)Функционалы и их

разновидности

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

Ю У р Г У

МехМат

С ПСодержание

1. Функционалы общие определения

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

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

3. Особые виды функционалов композиции

фильтры

редукции

2

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

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

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

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

Ю У р Г У

МехМат

С ППонятие отображения Отображения - эффективный механизм абстрагирования, моделирования, проектирования

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

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

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

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

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

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

4

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

Ю У р Г У

МехМат

С ППонятие функционала

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

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

5

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

Ю У р Г У

МехМат

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

Построить список из <голов> элементов списка(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)

6

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

Ю У р Г У

МехМат

С П

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

общую функцию 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)) ; Длины элементов Эти определения функций формально эквивалентны ранее приведенным - они сохраняют

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

7

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

Ю У р Г У

МехМат

С П

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

им значений, по заданным спискам имен и их значений: (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 . три))

8

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

Ю У р Г У

МехМат

С П

Функционал для покомпонентной обработки Теперь попробуем определить по аналогии универсальную функцию покомпонентной обработки

двух списков с помощью заданной функции 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)

9

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

Ю У р Г У

МехМат

С П

Пример применения функционала 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))

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

10

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

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

Виды функционалов2

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

Ю У р Г У

МехМат

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

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

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

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

12

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

Ю У р Г У

МехМат

С ПФункционал 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)

13

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

Ю У р Г У

МехМат

С ППримеры применения 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

14

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

Ю У р Г У

МехМат

С ПФункционал 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 

15

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

Ю У р Г У

МехМат

С ППример использования funcall

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

(defun map2 (f2 lst) (cond ((null lst) nil) (t (cons (funcall f2 (car lst) (cadr lst)); выполняем конкатенацию результаты вызова ф-ии ; f2 для головы и 2го элемента (map2 f2 (cddr lst))); с результатом рекурсивного вызова исходной ф-ии ; для «укороченного» хвоста ) ))

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

16

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

Ю У р Г У

МехМат

С ПОтображающие функционалы Важный класс функционалов используемых в лиспе - отображающие функционалы (МАР

функционалы) - функции, которые некоторым образом отображают (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)

17

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

Ю У р Г У

МехМат

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

встроенных. Согласно стандарту, в базовую реализацию языка Лисп обычно включены функционалы: 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)) 

18

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

Ю У р Г У

МехМат

С ПОтображающие функционалы 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)

19

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

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

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

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

Ю У р Г У

МехМат

С П

Зачем нужны лямбда-выражения, безымянные функции Определения в описанных примерах не вполне удобны по следующим причинам:

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

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

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

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

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

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

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

21

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

Ю У р Г У

МехМат

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

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

(defun f1 (x) (+ 1 (* x x)))> (mapcar 'f1 '(1 2 3))(2 5 10)(defun f2 (x) (+ 1 x))> (mapcar 'f2 '(1 2 3))(2 3 4) Таким образом определяется специальная функция, которая используется только в MAPCAR.

Более эффективно в этом случае использовать, т.н. лямбда выражения:> (mapcar '(lambda (x) (+ 1 (* x x))) '(1 2 3))> (mapcar '(lambda (x) (+ 1 x)) '(1 2 3))

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

Общая форма записи:(lambda (параметры) <тело функции>)

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

22

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

Ю У р Г У

МехМат

С П

Пример использования лямбда-выражений Пусть дана вспомогательная функция 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))

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

23

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

Ю У р Г У

МехМат

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

Ниже приведено определение функции 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))

24

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

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

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

4

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

Ю У р Г У

МехМат

С ПКомпозиции функционалов Вызовы функционалов можно объединять в более сложные структуры таким же образом, как и

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

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

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

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

(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))

26

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

Ю У р Г У

МехМат

С ПФильтры Фильтр отличается от обычного отображения тем, что окончательно собирает не все

результаты, а лишь удовлетворяющие заданному предикату.

Пример: Построить список голов непустых списков: ; временно голова размещается в список, ; чтобы потом списки сцепить(defun heads (xl) (map-ap `(lambda (x) (cond (x (cons (car x) NIL) ) ) ) xl) )

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

27

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

Ю У р Г У

МехМат

С ПРедукция Если нас интересуют некие интегральные характеристики результатов, полученных при

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

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

Пример: Подсчитать сумму элементов заданного списка.

28

(defun sum-el (xl) (cond ((null xl) 0 ) (xl (+ (car xl) (sum-el (cdr xl) ) ))))

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

(*) Перестроим такое определение, чтобы вместо <+> можно было использовать любую бинарную функцию: (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

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

Ю У р Г У

МехМат

С П

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

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

29

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

Ю У р Г У

МехМат

С ПЛитература

Л.В. Городняя. Основы функционального программирования – ИНТУИТ[ http://www.intuit.ru/department/pl/funcpl/ ]

Морозов М.Н. Функциональное программирование. Курс лекций - 1999-2001 г.[ http://www.mari-el.ru/mmlab/home/lisp/title.htm ]

30