Скачать презентацию Функциональное программирование F Списки Лекция 2 Мар ГТУ Скачать презентацию Функциональное программирование F Списки Лекция 2 Мар ГТУ

dd1f868bd3f8144a45d9742c246495fb.ppt

  • Количество слайдов: 50

Функциональное программирование F#. Списки Лекция 2 Мар. ГТУ, 2012 1 Функциональное программирование F#. Списки Лекция 2 Мар. ГТУ, 2012 1

Ø Декларация типов Ø Списки Ø Функции высшего порядка Ø Пример загрузка данных из Ø Декларация типов Ø Списки Ø Функции высшего порядка Ø Пример загрузка данных из файла Мар. ГТУ, 2012 2

Декларация типов Мар. ГТУ, 2012 3 Декларация типов Мар. ГТУ, 2012 3

Декларация типов Ø Объявление типа позволяет определить новые типы соответствующие обычным структурам данных используемым Декларация типов Ø Объявление типа позволяет определить новые типы соответствующие обычным структурам данных используемым в программах ü тип–произведение для кортежей или записей ü тип–сумма для union ü рекурсивный тип ü параметризованный тип ü функциональный тип type nom = typedef ; ; Мар. ГТУ, 2012 4

Декларация типов Ø Объявление типов по умолчанию рекурсивно type nom 1 = typedef 1 Декларация типов Ø Объявление типов по умолчанию рекурсивно type nom 1 = typedef 1 and nom 2 = typedef 2 : and nomn = typedefn ; ; Ø Параметризация переменной типа type 'a nom = typedef ; ; type ('a 1. . . 'an) nom = typedef ; ; Мар. ГТУ, 2012 5

Записи Ø Запись — это кортеж, в котором каждому полю присваивается имя. Запись всегда Записи Ø Запись — это кортеж, в котором каждому полю присваивается имя. Запись всегда соответствует объявлению нового типа. Для определения записи необходимо указать ее имя, а так же имя и тип для каждого поля записи. type nom = { nom 1 : t 1; . . . ; nomn : tn } ; ; let c = {re=2. ; im=3. } Мар. ГТУ, 2012 6

Пример let add_complex c 1 c 2 = {re=c 1. re+. c 2. re; Пример let add_complex c 1 c 2 = {re=c 1. re+. c 2. re; im=c 1. im+. c 2. im}; ; let mult_complex c 1 c 2 = match (c 1, c 2) with ({re=x 1; im=y 1}, {re=x 2; im=y 2}) -> {re=x 1*. x 2 -. y 1*. y 2; im=x 1*. y 2+. x 2*. y 1} ; ; Ø Преимущество записей по сравнению с кортежами ü более информативное описание, благодаря именам полей — это в частности позволяет облегчить образцы фильтра; ü идентичное обращение по имени, порядок значения не имеет — главное указать имя. Ø Подробнее смотрите пункт 1. 2. 3 «Записи» в документации языка Ocaml Мар. ГТУ, 2012 7

Сумма Ø тип сумма соответствует объединению множеств (union) type nom =. . . | Сумма Ø тип сумма соответствует объединению множеств (union) type nom =. . . | Nomi. . . | Nomj of tj. . . | Nomk of tk *. . . * tl. . . ; ; Ø Имя конструктора это специальный идентификатор. Ø Замечание ü Имя конструктора должна всегда начинаться с заглавной буквы. ü ключевое слово of указывает тип аргумента Мар. ГТУ, 2012 8

Рекурсивный тип Ø В отличии от объявления let, type всегда рекурсивный (необходим – для Рекурсивный тип Ø В отличии от объявления let, type всегда рекурсивный (необходим – для списков, деревьев и так далее) type int_or_char_list = Nil | Int_cons of int * int_or_char_list | Char_cons of char * int_or_char_list ; ; Мар. ГТУ, 2012 9

Видимость описания Ø На имена конструкторов распространяются те же правила, что и на глобальные Видимость описания Ø На имена конструкторов распространяются те же правила, что и на глобальные объявления. Переопределение скрывает своего предшественника. Скрытые значения всегда существуют, они никуда не делись. Ø Дома рассмотреть параметризованные, функциональные типы, видимость описания Ø (пункты 1. 2. 6 – 1. 2. 8) Мар. ГТУ, 2012 10

Списки Мар. ГТУ, 2012 11 Списки Мар. ГТУ, 2012 11

Примеры 1: : 2: : 3: : [] или [1; 2; 3] “hello”: : Примеры 1: : 2: : 3: : [] или [1; 2; 3] “hello”: : ”there”: : [] или [“hello”; ”there”] Ø : : – это бинарный оператор, синоним cons (constructor) Ø Все операции описаны в модуле List, поэтому при их использовании надо добавить в программу open List, либо указывать List. cons и т. д. Мар. ГТУ, 2012 12

Работа с головой и хвостом Ø Для отделения головы и хвоста обычно используют pattern Работа с головой и хвостом Ø Для отделения головы и хвоста обычно используют pattern matching let h: : t = [1; 2; 3]; ; match list with [] -> … | h: : t -> …; ; Ø Возможно также использовать функции hd (head для версии 2. 0) и tl (tail для версии 2. 0), а также cons для создания Мар. ГТУ, 2012 13

Рекурсивная обработка рекурсивной структуры Ø Структура данных рекурсивна, потому что она определяется через саму Рекурсивная обработка рекурсивной структуры Ø Структура данных рекурсивна, потому что она определяется через саму себя Ø Соответственно, для такой структуры хорошо подходит рекурсивная обработка с pattern matching let rec length l = match l with [] -> 0 | _: : t -> 1+length(t) ; ; Мар. ГТУ, 2012 let rec length = function [] -> 0 | _: : t -> 1+length(t); ; 14

Сумма элементов списка let rec sum L = match L with [x] -> x Сумма элементов списка let rec sum L = match L with [x] -> x | x: : t -> x+sum t; ; let rec sum = function [x] -> x | x: : t -> x+sum t; ; Ø sum [1; 2; 3] → 1+sum [2; 3] → 1+2+sum [3] → 1+2+3 → 6 Ø В модуле List определены более общие функции ü List. sum. By: (T→U) →T list →U Ø let sum = List. sum. By (fun x -> x) [4; 3; 6; 8]; ; Мар. ГТУ, 2012 15

Определение длины списка Ø Встроенная функция модуля List let rec length l = match Определение длины списка Ø Встроенная функция модуля List let rec length l = match l with [] -> 0 | _: : t -> 1+length(t) ; ; let rec length = function [] -> 0 | _: : t -> 1+length(t); ; Ø length [1; 2; 3] → 1+length [2; 3] → 1+1+length [3] → 1+1+1+length [] → 1+1+1+0 → 3 Мар. ГТУ, 2012 16

Принадлежность элемента списку Ø Встроенная функция модуля List let rec mem x = function Принадлежность элемента списку Ø Встроенная функция модуля List let rec mem x = function [] -> false | z: : t -> z=x or mem x t; ; Ø mem 2 [1; 2; 3] → (1=2) or mem 2 [2; 3] → (2=2) or mem 2 [3] → true Ø mem 4 [1; 2] → (4=1) or mem 4 [2] → (4=2) or mem 4 [] → false Мар. ГТУ, 2012 17

Конкатенация списков let rec append M L = match M with [] -> L Конкатенация списков let rec append M L = match M with [] -> L | h: : t -> h: : (append t L); ; Ø append [1; 2] [3; 4] → 1: : append [2] [3; 4] → 1: : 2: : append [] [3; 4] → 1: : 2: : [3; 4] = [1; 2; 3; 4] Ø Определена как оператор @ Ø [1; 2] @ [3; 4] → [1; 2; 3; 4] Мар. ГТУ, 2012 18

Удаление элемента по значению let rec remove x = function [] -> [] | Удаление элемента по значению let rec remove x = function [] -> [] | h: : t when h=x -> remove x t | h: : t -> h: : remove x t; ; Ø remove 3 [2; 3; 4; 3] → if 3=2 then remove 3 [3; 4; 3] else 2: : remove 3 [3; 4; 3] → 2: : remove 3 [4; 3] → 2: : 4: : remove 3 [3] → 2: : 4: : [] = [2; 4] Ø Определите функцию для удаления только первого вхождения элемента; для генерации списка всех возможных удалений одного вхождения элемента] Мар. ГТУ, 2012 19

Другие полезные функции Ø nth – взятие n-го элемента списка Ø concat – объединение Другие полезные функции Ø nth – взятие n-го элемента списка Ø concat – объединение списка списков в один список Мар. ГТУ, 2012 20

Функции высшего порядка Мар. ГТУ, 2012 21 Функции высшего порядка Мар. ГТУ, 2012 21

Итерация let rec iter f = function [] -> () | h: : t Итерация let rec iter f = function [] -> () | h: : t -> (f h); iter f t; ; Ø Функции с суффиксами -i, -2 и -i 2 доступны для многих библиотечных функций ü -i - функции-обработчику передается номер элемента ü -2 – функция работает с двумя параллельными списками Ø List. iteri (fun n x -> printf "%d. %sn" (n+1) x) ["One"; "Two"; "Three"]; ; Ø List. iter 2 (fun n x -> printf "%d. %sn" n x) [1; 2; 3] ["One"; "Two"; "Three"]; ; Мар. ГТУ, 2012 22

Нахождение элемента Ø find_index; find_indexi ü если элемент не находится, то происходит исключение Ø Нахождение элемента Ø find_index; find_indexi ü если элемент не находится, то происходит исключение Ø tryfind_index; tryfind_indexi type person = string*int; ; let plist = [("Vasya", 123); ("Petya", 234)]; ; let find_name no = List. tryfind (fun (name, num) -> num=no) plist; ; match (find_name 123) with None -> "No person found" | Some((name, num)) -> "The person is "+name; ; Мар. ГТУ, 2012 23

Применение функции к элементам списка let rec map f = function [] -> [] Применение функции к элементам списка let rec map f = function [] -> [] | h: : t -> (f h): : map f t; ; Ø map (fun x->2*x) [1; 2; 3] = [2; 4; 6] Ø mapi (fun i x -> i*x) [1; 2; 3] = [0; 2; 6] Ø map (fun (s: string)->s. Length) ["One"; "Two"; "Three"] = [3; 3; 5] Ø ["One"; "Two"; "Three"] |> map (fun s->s. Length) Мар. ГТУ, 2012 24

Фильтрация списка let rec filter p = function [] -> [] | x: : Фильтрация списка let rec filter p = function [] -> [] | x: : t when p(x) -> x: : filter p t | x: : t -> filter p t; ; let sp 1 = [7; 4; 3; 6; 8] let sp 2 = List. filter(fun (nm) -> nm <= 4) sp 1 printf "sp 2 = %A nn" sp 2; Результат: [4; 3] Мар. ГТУ, 2012 25

Сворачивание списка Ø let sum = fold (fun ac x -> ac+x) 0; ; Сворачивание списка Ø let sum = fold (fun ac x -> ac+x) 0; ; Ø let prod = fold (fun ac x -> ac*x) 1; ; Ø let sum. By. Int f = fold (fun ac x ->ac*(f x)) 0; ; Ø let sum = reduce (fun u v -> u+v); ; Мар. ГТУ, 2012 26

Логические функции Ø let exists p = fold_left (fun a x -> a or Логические функции Ø let exists p = fold_left (fun a x -> a or x) false Ø let for_all p = fold_left (fun a x -> a and x) true Мар. ГТУ, 2012 27

Другие полезные функции Ø zip / unzip (zip 3/unzip 3) – объединить два (три) Другие полезные функции Ø zip / unzip (zip 3/unzip 3) – объединить два (три) списка в список пар (троек) и наоборот Ø assoc a L = snd (find (fun (u, v) -> u=a) L) Ø choose Ø sort / stable_sort – сортировка Ø partition – разбиение на 2 списка в зависимости от предиката Ø scan_left, scan_right – поэлементная обработка списка (map) с аккумулятором Мар. ГТУ, 2012 28

Пример использования библиотечных функций Ø permute L – генерация списка всех перестановок списка L Пример использования библиотечных функций Ø permute L – генерация списка всех перестановок списка L Ø permute [1; 2; 3] = [[1; 2; 3]; [1; 3; 2]; …; [3; 2; 1]] Ø Декомпозиция задачи: ü Рекурсия: как свести задачу к меньшей размерности? ü Отделяем голову, получаем все перестановки хвоста, затем ставим голову на все возможные позиции в этих перестановках ü Задача сводится к функции insertions, генерирующей список возможных «вставлений» элемента в список Мар. ГТУ, 2012 29

Генерация вставления Ø insertions 3 [1; 2] = [[3; 1; 2]; [1; 3; 2]; Генерация вставления Ø insertions 3 [1; 2] = [[3; 1; 2]; [1; 3; 2]; [1; 2; 3]] Ø Для пустого списка – одно возможное решение [[x]] Ø Для списка h: : t ü Вставляем всеми способами в t ü К этим спискам приписываем h в начало ü Добавляем решение – вставка в начало let rec insertions x = function [] -> [[x]] | h: : t -> (x: : h: : t): : (map (fun z -> h: : z) (insertions x t)); ; Мар. ГТУ, 2012 30

Перестановки Ø Для пустого списка – [[]] ü На произвольном шаге ü Получаем список Перестановки Ø Для пустого списка – [[]] ü На произвольном шаге ü Получаем список перестановок хвоста ü Каждую из перестановок надо раскрыть в список вставок, получившиеся списки объединить ü Для этого подходит функция map_concat let rec permute L = match L with [] -> [[]] | h: : t -> map_concat (fun z -> insertions h z) (permute t); ; Мар. ГТУ, 2012 31

Еще один способ вычисления факториала Ø let fact n = length (permute [1. . Еще один способ вычисления факториала Ø let fact n = length (permute [1. . n]); ; Мар. ГТУ, 2012 32

Списковое представление Ø type image = char list; ; Ø let mklist (s: string) Списковое представление Ø type image = char list; ; Ø let mklist (s: string) = [ for c in s. To. Char. Array() -> c ]; ; let sample = [ mklist ". . . "; mklist ". . . ##. . . "; mklist ". . . . . ##. . . . "; mklist ". . #. ##. . . "; mklist ". . . ##. . "; mklist ". . ##. "; mklist ". . . "]; ; Мар. ГТУ, 2012 33

Генерация элементов списка (list comprehension) Ø List. init 9 (fun x -> 2. 0**float(x))= Генерация элементов списка (list comprehension) Ø List. init 9 (fun x -> 2. 0**float(x))= [1; 2; 4; 8; 16; 32; 64; 128; 256] Ø [ for x in 0. . 8 -> 2. 0**float(x)] Ø [ 1. . 10 ] Ø [ 1. . 2. . 10 ] = [1; 3; 5; 7; 9] Ø [ for x in 1. . 10 when x%2=0 for y in 1. . 10 when y%2=0 -> (x, y) ]; ; Ø List comprehension можно использовать вместо map: ü [for x in L -> x*2] ü map (fun x->x*2) L Мар. ГТУ, 2012 34

Хвостовая рекурсия. Приведение к ней Мар. ГТУ, 2012 35 Хвостовая рекурсия. Приведение к ней Мар. ГТУ, 2012 35

Хвостовая рекурсия Ø Во многих случаях (особенно при обработке списков) рекурсивные алгоритмы могут быть Хвостовая рекурсия Ø Во многих случаях (особенно при обработке списков) рекурсивные алгоритмы могут быть сведены к итерационным Ø Такая рекурсия называется хвостовой ü Линейная ü Рекурсивный вызов – в конце тела функции Øт. е. вызов совершается сразу после выполнения тела функции ü Не выделяется промежуточная память Мар. ГТУ, 2012 36

length let rec length = function [] -> 0 | _: : t -> length let rec length = function [] -> 0 | _: : t -> 1+length(t); ; Ø После рекурсивного вызова выполняется операция сложения Ø При каждом погружении выделяется память на промежуточные вычисления и на адрес возврата Ø В случае итерационного алгоритма выделение памяти бы не потребовалось Мар. ГТУ, 2012 37

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

Хвостовая рекурсия let rec len a = function [] -> a | _: : Хвостовая рекурсия let rec len a = function [] -> a | _: : t -> len (a+1) t Ø Сложение выполняется перед рекурсивным вызовом Ø Результат возвращается напрямую в вызывающую функцию => не требуется выделения памяти Мар. ГТУ, 2012 39

Реверсирование списка let rec rev = function [] -> [] | h: : t Реверсирование списка let rec rev = function [] -> [] | h: : t -> (rev t)@[h]; ; Ø Не хвостовая рекурсия Ø Сложность: порядка n*n, поскольку append имеет линейную сложность Мар. ГТУ, 2012 40

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

Порядковое представление списков Ø Возможно предложить другое, более «функциональное» определения списка как последовательности чисел Порядковое представление списков Ø Возможно предложить другое, более «функциональное» определения списка как последовательности чисел 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)); ; Мар. ГТУ, 2012 42

Например let rec to_list l = if nempty l then [] else (nhd l): Например 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)); ; Ø Порядковое представление неэффективно (представление функции в памяти), но любопытно с теоретической точки зрения Ø Может использоваться для представления матриц (в особенности разреженных) Мар. ГТУ, 2012 43

Матрицы. Массивы Мар. ГТУ, 2012 44 Матрицы. Массивы Мар. ГТУ, 2012 44

Представление матриц Ø Списком списков type ‘a matrix = ‘a list; ; Ø Порядковое Представление матриц Ø Списком списков type ‘a matrix = ‘a list; ; Ø Порядковое представление type ‘a rmatrix = int->’a option; ; Ø Удобно для разреженных матриц, при этом надо отдельно хранить размерность: type ‘a rmatrix = int*(int->’a option); ; Мар. ГТУ, 2012 45

Массивы Ø F# – частично-императивный язык, содержит mutable-типы данных Ø Тип массив во многом Массивы Ø F# – частично-императивный язык, содержит mutable-типы данных Ø Тип массив во многом напоминает список, но является mutable: ü Операции не возвращают новый массив, а модифицируют исходный Ø 2 вида многомерных массивов: ü Jagged (непрямоугольный) и regular ü Представление матриц и векторов Мар. ГТУ, 2012 46

Пример загрузка данных из файла Мар. ГТУ, 2012 47 Пример загрузка данных из файла Мар. ГТУ, 2012 47

Пример загрузка данных из файла let img 2 = new Bitmap( @ Пример загрузка данных из файла let img 2 = new Bitmap( @"d: image. bmp"); let img 3 = new Bitmap(400, 400) let n = 400; for i=0 to n-1 do for j=0 to n-1 do let color = img 2. Get. Pixel(i, j); if color. Is. Empty = false && color. A > (byte) 20 && color. R = (byte) 0 && color. G = (byte) 0 && color. B = (byte) 0 then img 3. Set. Pixel(i, j, Color. Blue); else img 3. Set. Pixel(i, j, Color. Floral. White); img 3. Save(@"d: image 2. bmp") Мар. ГТУ, 2012 48

Итог Ø Рекурсивные структуры данных – часто используемый удобный механизм хранения данных в функциональном Итог Ø Рекурсивные структуры данных – часто используемый удобный механизм хранения данных в функциональном программировании Ø Списки – гармоничная часть языка ü Прозрачный синтаксис (специальный синтаксис конструкторов) ü Поддержка со стороны библиотеки Ø Множество абстрактных функций для обработки списков позволяет, комбинируя их, легко реализовывать различные алгоритмы обработки Мар. ГТУ, 2012 49

Спасибо за внимание! Вопросы? Мар. ГТУ, 2012 50 Спасибо за внимание! Вопросы? Мар. ГТУ, 2012 50