Функциональное программирование
DESCRIPTION
Функциональное программирование. Факультет инноваций и высоких технологий Московский физико-технический институт. Лекция 10. Хвостовая рекурсия. Порядковое представление списков и матриц. Хвостовая рекурсия. - PowerPoint PPT PresentationTRANSCRIPT
Сошников Дмитрий Валерьевич
к.ф.-м.н., доцент[email protected]
Факультет инноваций и высоких технологий
Московский физико-технический институт
Лекция 10
Хвостовая рекурсия. Порядковое представление списков и матриц.
3
©20
08 С
ошни
ков
Д.В
.
Во многих случаях (особенно при обработке списков) рекурсивные алгоритмы могут быть сведены к итерационным
Такая рекурсия называется хвостовой Линейная Рекурсивный вызов – в конце тела функции
▪ т.е. вызов совершается сразу после выполнения тела функции
Не выделяется промежуточная память
4
©20
08 С
ошни
ков
Д.В
.
После рекурсивного вызова выполняется операция сложения
При каждом погружении выделяется память на промежуточные вычисления и на адрес возврата – O(n)
В случае итерационного алгоритма выделение памяти бы не потребовалось
let rec length = function [] -> 0 | _::t -> 1+length(t);;
length [a;b;c]
length [b;c]
length [c]
length []
0
1+0=1
1+1=2
1+2=3
5
©20
08 С
ошни
ков
Д.В
.
Вводим промежуточную функцию len : int → T list → int
При каждом откусывании головы числовой параметр (разностный счетчик) увеличивается, затем делается рекурсивный вызов
В конце значение счетчика возвращается как результат
let length L = let rec len a = function [] -> a | _::t -> len (a+1) t in len 0 L;;
6
©20
08 С
ошни
ков
Д.В
.
Сложение выполняется перед рекурсивным вызовом Результат возвращается напрямую в вызывающую
функцию => не требуется выделения памяти Требуемая память – O(1)
let rec len a = function [] -> a | _::t -> len (a+1) t
len 0 [a;b;c]
len 1 [b;c]
len 2 [c]
len 3 []
3
3
3
3
3
7
©20
08 С
ошни
ков
Д.В
.
rev: T list → T listrev: T list → T list
let rec rev = function [] -> []| h::t -> (rev t)@[h];;
• Не хвостовая рекурсия• Сложность: O(n2), поскольку append имеет линейную
сложность
©20
08 С
ошни
ков
Д.В
.
8
Вводим аналог счетчика – списковый Начиная с [ ], каждое откусывание головы
присоединяется к этому списку
let rev L = let rec rv s = function [] -> s | h::t -> rv (h::s) t in rv [] L;;
©20
08 С
ошни
ков
Д.В
.
9
Возможно предложить другое, более «функциональное» определения списка как последовательности чисел
list : int -> A
type 'a nlist = int->'a option ;;
let nhd l = l 0;;let ntl l = fun x -> if x>=0 then l (x+1) else None;;let nempty l = (l 0) = None;;let mempty = fun _ -> None;;let ncons a l = fun x ->
(if x=0 then Some(a) else l(x-1));;
• Возможно работать с таким списком привычным образом, определив основные операции
©20
08 С
ошни
ков
Д.В
.
10
Другие примеры – на семинаре и в рамках лабораторной работы №1
Порядковое представление неэффективно (представление функции в памяти), но любопытно с теоретической точки зрения
Может использоваться для представления матриц (в особенности разреженных)
let rec to_list l = if nempty l then [] else (nhd l)::to_list (ntl l)
let rec map f l = match nhd l with None -> mempty | Some(x) -> ncons (f x) (map f (ntl l));;
let rec from_list = function [] -> mempty| h::t -> ncons h (from_list t)
©20
08 С
ошни
ков
Д.В
.
11
Удобно для разреженных матриц, при этом надо отдельно хранить размерность:▪ type ‘a rmatrix = int*int*(int->int->’a option);;
Когда использовать то или иное представление?
12
©20
08 С
ошни
ков
Д.В
.
Списковое транспонирование оказывается менее очевидной операцией, чем определение aij=bji
А соответственно и другие операции, использующие столбцы (умножение), будут сложнее (хотя могут быть сведены к транспонированию)
let rec slice = function [] -> ([],[])| h::t -> let (u,v) = slice t in (hd h::u, tl h::v);;
[[1 2 3] [[1 4 7] [4 5 6] [2 5 8] [7 8 9]] [3 6 9]]
let rec trans = function []::_ -> [] | x -> let (h,r) = slice x in h::(trans r);;
13
©20
08 С
ошни
ков
Д.В
.
Мораль: Для различных задач оказываются
удобными различные представления абстрактных типов данных
let transp (n,m) = (n, fun i j -> m j i);;
14
©20
08 С
ошни
ков
Д.В
.
Matrix<T>, Vector<T>, RowVector<T>matrix = Matrix<float>, rowvec, vector
#r "FSharp.PowerPack.dll"open Microsoft.FSharp.Core;;open Microsoft.FSharp.Math;;let M = Matrix.of_list [[1.0;2.0;3.0];[4.0;5.0;6.0];[7.0;8.0;9.0]];;M.Transpose;;M.Row(1);;(Vector.of_list [1.0;2.0;3.0]) *
(RowVector.of_list [1.0;2.0;3.0]);;M*M.Transpose;;
15
©20
08 С
ошни
ков
Д.В
.
Операция Список Массив со случайным доступом
Случайный доступ O(n) O(1)
Поиск O(n) O(n)
Вставка элемента в начало / удаление первого элемента
O(1) O(n)
Вставка / удаление элемента в конец (середину)
O(n) O(n)
Реверсирование O(n) [O(n2)] O(n)