1 / 15

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

Функциональное программирование. Факультет инноваций и высоких технологий Московский физико-технический институт. Лекция 10. Хвостовая рекурсия. Порядковое представление списков и матриц. Хвостовая рекурсия.

Download Presentation

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

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. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Функциональное программирование Факультет инноваций и высоких технологий Московский физико-технический институт

  2. Лекция 10 Хвостовая рекурсия. Порядковое представление списков и матриц.

  3. Хвостовая рекурсия • Во многих случаях (особенно при обработке списков) рекурсивные алгоритмы могут быть сведены к итерационным • Такая рекурсия называется хвостовой • Линейная • Рекурсивный вызов – в конце тела функции • т.е. вызов совершается сразу после выполнения тела функции • Не выделяется промежуточная память

  4. length • После рекурсивного вызова выполняется операция сложения • При каждом погружении выделяется память на промежуточные вычисления и на адрес возврата – O(n) • В случае итерационного алгоритма выделение памяти бы не потребовалось 1+2=3 let rec length = function [] -> 0 | _::t -> 1+length(t);; length [a;b;c] 1+1=2 length [b;c] 1+0=1 length [c] 0 length []

  5. Приведение к хвостовой рекурсии • Вводим промежуточную функцию len : int → T list → int • При каждом откусывании головы числовой параметр (разностный счетчик) увеличивается, затем делается рекурсивный вызов • В конце значение счетчика возвращается как результат let length L = let rec len a = function [] -> a | _::t -> len (a+1) t in len 0 L;;

  6. Хвостовая рекурсия 3 • Сложение выполняется перед рекурсивным вызовом • Результат возвращается напрямую в вызывающую функцию => не требуется выделения памяти • Требуемая память – O(1) len 0 [a;b;c] let rec len a = function [] -> a |_::t -> len (a+1) t 3 len 1 [b;c] 3 3 len 2 [c] 3 len 3 []

  7. Реверсирование списка rev: T list → T list let rec rev = function [] -> [] | h::t -> (rev t)@[h];; • Не хвостовая рекурсия • Сложность: O(n2), поскольку append имеет линейную сложность

  8. Приведение к хвостовой рекурсии • Вводим аналог счетчика – списковый • Начиная с [], каждое откусывание головы присоединяется к этому списку let rev L = let rec rv s = function [] -> s | h::t -> rv (h::s) t in rv [] L;;

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

  10. Например • Другие примеры – на семинаре и в рамках лабораторной работы №1 • Порядковое представление неэффективно (представление функции в памяти), но любопытно с теоретической точки зрения • Может использоваться для представления матриц (в особенности разреженных) let rec to_list l = if nempty l then [] else (nhd l)::to_list (ntl l) let rec from_list = function [] -> mempty | h::t -> ncons h (from_list t) let rec map f l = match nhd l with None -> mempty | Some(x) -> ncons (f x) (map f (ntl l));;

  11. Представление матриц • Удобно для разреженных матриц, при этом надо отдельно хранить размерность: • type ‘a rmatrix = int*int*(int->int->’a option);; • Когда использовать то или иное представление?

  12. Транспонирование [[1 2 3] [[1 4 7] [4 5 6] [2 5 8] [7 8 9]] [3 6 9]] • Списковое транспонирование оказывается менее очевидной операцией, чем определение aij=bji • А соответственно и другие операции, использующие столбцы (умножение), будут сложнее (хотя могут быть сведены к транспонированию) let rec slice = function [] -> ([],[]) | h::t -> let (u,v) = slice t in (hd h::u, tl h::v);; let rec trans = function []::_ -> [] | x -> let (h,r) = slice x in h::(trans r);;

  13. Транспонирование порядковой матрицы • Мораль: • Для различных задач оказываются удобными различные представления абстрактных типов данных let transp (n,m) = (n, fun i j -> m j i);;

  14. Встроенные линейно-алгебраические типы • 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. Подводим итоги

More Related