Функциональное представление данных Файл

advertisement
15.11.09
Функциональное представление данных
План лекции :
1. Введение, наводящие соображения.
2. Функциональное представление множеств.
3. Функциональное представление списков.
4. Функциональное представление булевых констант.
5. Функциональное представление натуральных чисел.
Литература по лекции
Примечания и комментарии
1. Введение, наводящие соображения
При решении конкретной задачи всегда приходится выбирать некоторое математическое представление объектов участвующих в исследуемом процессе. В языке LISP для этих целей обычно используются списки и обработка различных данных сводится к программам обработки списков. Однако, в функциональном программировании для представления объектов можно использовать функции, соответствующие основным свойствам объектов и допустимым операциям над ними.
2. Функциональное представление множеств
Если множество это совокупность объектов без конкретизации деталей вхождения этих объектов (количество вхождений, порядок следования, …), то основной операцией для множества будет операция проверки вхождения элемента в множество. Ранее мы определили функцию in S x , которая возвращает значение «истина» или «ложь» в зависимости от того, содержится элемент x в множестве S или нет. Все другие операции над множествами мы определяли через функцию inS x .
Сопоставим множеству S функцию S x  и определим, что функция S x  возвращает значение «истина» если объект x принадлежит множеству и «ложь» в противном случае. И в дальнейшем вместо функции in S x будем использовать 15.11.09
Функциональное представление данных
функцию S x  . Таким образом мы приходим к функциональному представлению множества: в программах вместо множества как структуры данных S мы будем использовать функцию S x  .
При решении конкретных задач нет необходимости в перечислении всех элементов множества, достаточным знать: принадлежит тот или иной объект множеству или нет. В этом случае множество может быть адекватно представлено в виде функции одного аргумента. Эта функция должна принимать значение "истина", если ее аргумент принадлежит множеству и значение "ложь" в противном случае. Понятно, что разным множествам будут соответствовать разные функции и при изменении множества (добавление или удаление элементов) соответствующая функция должна меняться.
Начнём с определения пустого множества:
S0 = λx.nil
Применение функции S0 к любому аргументу даст значение nil :
(S0 a) -> nil
Теперь определим функцию AddElem , которая позволяет добавить новый элемент e в множество S :
AddElem = λS.λe.(if (S e) S (λx.(if (eq x e) T (S x))))
При помощи функции AddElem определим множество S1 , состоящее из элементов a , b и c :
S1 = (AddElem (AddElem (AddElem S0 a) b) c)
15.11.09
Функциональное представление данных
Операции объединения Union и Intersect пересечения множеств можно определить следующим образом:
Union = λS1.λS2.λx.(or (S1 x) (S2 x))
Intersect = λS1.λS2.λx.(and (S1 x) (S2 x))
3. Функциональное представление списков
В LISP'е (где списки являются основной формой представления данных) и других языках программирования для реализации списков обычно используется динамическая структура, отдельные элементы которой связаны между собой посредством ссылок. Такое представление используется настолько часто, что почти стало синонимом списка. Каждый раз, когда встречается слово "список", возникает картинка более или менее похожая на +---+---+
+---+---+
+---+---+
|
| --+--->|
| --+---> . . . --->|
|nil|
+---+---+
+---+---+
+---+---+
В связи с этим напомним, что список, по определению, может быть пустым или состоять из "головы" и "хвоста", "хвост" тоже является списком. Любой объект, соответствующий приведенному определению, будет адекватным представлением списка.
Введем функцию одного аргумента и будем считать, что при значении аргумента CAR значением функции будет голова списка, а при значении CDR ­ хвост списка. Такая функция может быть использована в качестве представления списка. Любому списку будет соответствовать своя функция и при изменении списка соответствующая функция будет меняться. Вначале введем функцию соответствующую пустому списку:
15.11.09
Функциональное представление данных
Empty = λS.nil
(Empty CAR) -> nil
(Empty CDR) -> nil
Затем, зададим функциональный аналог функции­конструктора Cons , которая создаёт список из его "головы" и "хвоста": ConsList = λh.λt.(lambda (x) (if (equal x 'CAR) h t)))
Функция Cons позволяет конструировать различные списки, например :
Ln = (Cons 1 (Cons 2 (Cons 3 EMPTY)))
La = (Cons A (Cons B (Cons C EMPTY)))
Вызывая функции Ln и La с различными комбинациями аргументов CAR и CDR мы можем получить доступ к различным элементам списка: (Ln CAR) -> 1
((Ln CDR) CAR) -> 2
(((Ln CDR) CDR) CAR) -> 3
Можно определить и другие функции для работы со списками, подобно функции Cons это будут функции высших порядков, так как в качестве аргумента они должны получать список представленный в виде функции и (или) своим значением могут иметь соответствующую функцию. Функция Append осуществляет конкатенацию двух 15.11.09
Функциональное представление данных
списков :
Append = λl1.λl2.
(if (Null l1) l2 (Cons (l1 CAR) (Append (l1 CDR) l2)) )
Здесь функция Null проверяет, является список пустым или нет :
Null = λl.(and (not (l CAR)) (not (l CDR)))
Также можно определить функцию инверсии списка
Reverse = λl1.λl2.
(if (Null l1) l2 (Reverse (l1 CDR) (Cons (l1 CAR) l2)) )
и многие другие функции.
4. Функциональное представление булевых констант
В виде функций можно представить не только структуры данных подобные множествам и спискам, но и скалярные объекты подобные былевым константам, натуральным числам и т.п. .
Начнём с рассмотрения встроенной функции if P Q R которая возвращает Q или R в зависимости от значения предиката P . В этом случае предикат условной функции можно рассматривать как селектор, который выбирает одно из выражений Q или R . Выразим это явно, объединив в одной функции функцию if и предикат P . Так как предикат P может возвращать два значения, то в результате получаем две функции:
if TRUE P QTrue P Q и if FALSE P Q False P Q
15.11.09
Функциональное представление данных
В соответствии с определением if функции True и False возвращать следующие значения:
True P Q P и False P Q Q
Таким образом мы приходим к функциональному представлению булевых констант TRUE и FALSE .
Теперь необходимо определить функции предикаты так, чтобы вместо константы TRUE или FALSE они возвращали соответствующую функцию и тогда вместо функции if можно использовать непосредственно функцию предикат. Например:
(if (and A B) Q R) -> ((And A B) Q R)
В соответствии с указанными свойствами функций True P Q P и False P Q Q эти функции можно определить следующим образом:
True = λx.λy.x
False = λx.λy.y
Определения функций предикатов вытекает из их свойств:
Not = λx.(x False True)
Or = λx.λy.(x True y)
And = λx.λy.(x y False)
Убедиться в справедливости этих определений можно непосредственной проверкой результатов действия функций Not , Or и And для различных значений их аргументов. Так например:
15.11.09
Функциональное представление данных
Not True ->
->
->
->
->
(λx.(x False True)) True
(λx.(x False True)) True
(True False True)
((λx.λy.x) False True)
False
And True True -> (λx.λy.(x y False)) True True
-> (True True False)
-> ((λx.λy.x) True False)
-> True
And False True -> (λx.λy.(x y False)) False True
-> (False True False)
-> ((λx.λy.y) True False)
-> False
и так далее.
Примеры применения:
(TRUE 1 2) -> 1
(FALSE 1 2) -> 2
((AND TRUE TRUE) 1 2) -> 1
5. Функциональное представление натуральных чисел
15.11.09
Функциональное представление данных
Если отвлечься от представления входных и выходных данных в программах, то с точки зрения реализации алгоритма решения задачи основная цель использования чисел это указать, сколько раз должна быть выполнена некоторая операция. C0 = λs.λx.x
C1 = λs.λx.(s x)
C2 = λs.λx.(s(s x))
C3 = λs.λx.(s(s(s x)))
. . .
Cn = λs.λx.(s(. . .(s x) . . .))
Проверка равенства нулю: ( ? ? ? ? ? )
IsZero = λn.n (True False) True
IsZero C0
-> (λn.n (True False) True) C0
-> C0 (True False) True
-> (λs.λx.x) (True False) True
-> True
IsZero C1
-> (λn.n (True False) True) C1
-> C1 (True False) True
-> (λs.λx.(s x)) (True False) True
-> (True False) True
-> (λx.λy.x False) True
15.11.09
Функциональное представление данных
-> True False
-> λx.λy.x False
-> False
Функция инкремент
Inc = λa.λb.λc.b(a b c)
Inc Cn
-> (λa.λb.λc.b(a b c)) Cn
-> λb.λc.b(Cn b c)
-> λb.λc.b((λs.λx.(s(. . .(s x) . . .))) b c)
-> λb.λc.b(b(. . .(b c) . . .)
-> Cn+1
Функция вычисления суммы
Add = λa.λb.λc.λd.(a c)(b c d)
Add Cn Cm
-> (λa.λb.λc.λd.(a c)(b c d)) Cn Cm
-> λc.λd.(Cn c)(Cm c d)
-> λc.λd.(λs.λx.(s(. . .(s x) . . .)) c)(Cm c d)
-> λc.λd.(λx.(c(. . .(c x) . . .)))(Cm c d)
n-раз
-> λc.λd.(c(. . .(c (Cm c d)) . . .))
n-раз
15.11.09
Функциональное представление данных
-> λc.λd.(c(. . .(c (λs.λx.(s(. . .(s x) . . .)) c d)) . . .))
n-раз
m-раз
-> λc.λd.(c(. . .(c (с(. . .(c d) . . .)) ) . . .))
n-раз
m-раз
-> λc.λd.(c(. . .(c d) . . .))
n+m-раз
-> Cn+m
Обратите внимание на то, что при выводе результата действия функций Not , Add ,
Inc , Add , ... мы использовали формальные правила редукции λ­выражений и получали разумные результаты. Это дополнительно свидетельствует в пользу использования λ­исчисления в качестве модели вычислений.
Список литературы по лекции
1. А. Филд, П. Харрисон «Функциональное программирование» Москва, Мир, 1993 (глава 6)
2. П. Хендерсон «Функциональное программирование. Применение и реализация» Москва, Мир, 1993 (глава 9.1)
Download