Post on 16-Jun-2015
description
1
Функциональное программировани
е на F#
Богомолов Юрийyuriy.bogomolov@gmail.com
2
Общие сведенияF# — мультипарадигмальный язык:• Функциональное программирование:
o Строго типизированный язык с возможностью выведения типов;
o Поддерживает каррирование функций, лямбда-функции, замыкания, композицию функций;
o Использование совпадений с образцом (pattern-matching);
o Алгебраические типы данных (discriminated unions).
3
Общие сведенияF# — мультипарадигмальный язык:• Императивное программирование:
o Изменяемые данные;o Управление потоком выполнения (условные
переходы, циклы);o Изменяемые массивы, списки и словари;o Возможность использования изменяемых
переменных.
4
Общие сведенияF# — мультипарадигмальный язык:• Объектно-ориентированное
программирование:o Поддержка пользовательских типов данных
(записи, кортежи, классы);o Классы:
• Наследование;• Перегрузка операторов;
o Последовательные, параллельные, асинхронные вычисления.
5
Типы данныхТип данных Название
Целочисленныйsbyte, byte, int16, uint16, int/int32,
uint32, int64, uint64, bigint
С плавающей точкой float32, float, decimal
Строковый char, string
Логический bool
Специальныеunit, кортежи,
последовательности, списки, размеченные объединения
6
• Назначение символьного имени (≈ определение переменной):let x = 1let y = 2Или проще: let x, y = 1, 2let z = x + y
• Определение функций:let add x y = x + ylet addOne = add 1o Вызов:
let sumXY = add x y // sumXY = 3let sumX1 = addOne x // addX1 = 2
F#: функциональная парадигма
7
• Строгая типизация:let sign num =
if num > 0 then "positive" elif num < 0 then "negative" else 0;; // error FS0001 — несовместимость типов
• Внутренние функции:let addOne x =
let add a b = a + badd 1 x;;
• Рекурсия:let rec fact n =
if n = 1 then 1 else fact (n – 1);;
F#: функциональная парадигма
8
• Хвостовая рекурсия — оптимальная рекурсия для компилятора, т.к. преобразовывается в цикл.
• Применяется функциональный паттерн с применением внутренней функции и аккумулятора:
let fib n =let loop acc1 acc2 = function
| n when n = 0I -> acc1| n -> loop acc2 (acc1+acc2) (n-
1I)loop 0I 1I n
F#: функциональная парадигма
• Функция, имеющая тело, но не имеющая имени.
• Аналог из C# — анонимные делегаты.• Синтаксис:(fun arg1 arg2 ... -> expr)let x = (fun x -> x * x) 2 // возвратит 4
9
F#: функциональная парадигма.
Лямбда-функции
10
• Вычисление N-ного числа Фибоначчи: let fib n =
match n with| 0 -> 0| 1 -> 1| _ -> fib (n – 1) + fib (n – 2)
• Условия совпадения можно комбинировать:let rec factorial n = match n with
| 0 | 1 -> 1| _ -> n * factorial (n – 1)
F#: функциональная парадигма.
Совпадение с образцом
11
• Связывание переменных на лету:let rec fact = function
| 0 | 1 -> 1| n -> n * fact (n - 1)
• Использование guard’ов:let sign = function
| 0 -> 0| x when x < 0 -> -1| x when x > 0 -> 1
F#: функциональная парадигма.
Совпадение с образцом
12
• let numbers = [1; 2; 3; 4; 5]• let numbers = [1..2..5] // аналогично ↑• Оператор :: ("cons"):
let primes = 1 :: 3 :: 5 :: 7 :: []• Использование List.init:
List.init 5 (fun x -> x + 1) // [1; 2; 3; 4; 5]List.init 5 (fun x -> x, x*x, x*x*x)
//[(0,0,0);(1,1,1);(2,4,8);(3,9,27);(4,16,64)]
F#: функциональная парадигма.
Работа со списками
13
• Использование генераторов:o Синтаксис: [for x in collection do … yield expr][for a in 1..10 do yield (a, a*a)] // [(1,1), (2,4), (3,9), ...][for a in 1..3 do yield! [a..a+3]] // [1,2,3,4, 2,3,4,5, 3,4,5,6]
• Совпадение с образцом:let rec sumList list =
match list with| [] -> 0| head::tail -> head + sumList tail
let reverse list = let rec loop acc = function | [] -> acc | hd :: tl -> loop (hd :: acc) tlloop [] list
F#: функциональная парадигма.
Работа со списками
14
• Использование модуля List:o rev — обращение списка:List.rev [1..5] // [5;4;3;2;1]o filter — фильтрация списка:List.filter (fun x -> x % 2 = 0) [1..10] // [2;4;6;8;10]o map — применение функции к каждому элементу списка:List.map (fun x -> x*x) [1..5] // [1;4;9;16;25]o append (оператор @):let numbers = [1..5] @ [6..10] // [1;2;3;4;5;6;7;8;9;10]o find и tryFind:List.find (fun x -> x = 1) [1..10] // возвратит 1List.find (fun x -> x = 0) [1..10] // ошибка KeyNotFoundExceptionList.tryFind (fun x -> x = 0) [1..10] // возвратит Noneo fold и foldBackList.fold (+) 0 [1..100] //5050
F#: функциональная парадигма.
Работа со списками
15
• Являются алгебраическим типом данных.• Представляют собой определенный набор
вариантов выбора. Дают возможность выявить ошибки на этапе компиляции.
• Синтаксис:
type unionName =| Case1| Case2 as datatype| …
F#: функциональная парадигма.
Размеченные объединения
16
• Пример: "выключатель"
type switchState =| On| Off| Ranged of float
let toggle = function| On -> Off| Off -> On| Ranged(brightness) -> 1.0 - brightness
let x = Onlet y = toggle xlet z = toggle Ranged(0.3)
F#: функциональная парадигма.
Размеченные объединения
17
• Пример: двоичные деревья
type tree = | Leaf of int| Node of tree * tree
let countLeaves tree = let rec loop acc tree =
match tree with| Leaf(_) -> acc + 1| Node(tree1, tree2) -> (loop acc tree1)
+ (loop acc tree2)loop 0 tree
F#: функциональная парадигма.
Размеченные объединения
18
• Изменяемые данные:• Ключевое слово mutable:let mutable x = 1x <- 10
• Ключевое слово ref:let x = ref 1 // x = ref int {contents = 1}
Обращение к ссылочному типу данных:let y = !x + 1 // y = 2
Присвоение:x := 2 // x = ref int {contents = 2}
F#: императивная парадигма
19
• Пример: функция статического инкремента (а-ля "генератор ID")
let incr = let counter = ref 0 fun () -> counter := !counter + 1 !counterlet id1 = incr() // id1 = 0let id2 = incr() // id2 = 1let id3 = incr() // id3 = 2let id4 = incr() // id4 = 3
F#: императивная парадигма
20
• Цикл for диапазонов:
for var = start-expr to end-expr do ... // тело цикла
for i = 1 to 5 doprintfn "i: %i" i
Выведет:i: 1i: 2i: 3i: 4i: 5
F#: императивная парадигма.
Циклы
21
• Цикл for для коллекций и последовательностей:
for pattern in expr do ... // тело цикла
let customers = ["John", 21; "Mary", 18; "Katy", 20]for (name, age) in customers do
printfn "Name: %s, age: %d" name age
Выведет:Name: John, Age: 21Name: Mary, Age: 18Name: Katy, Age: 20
F#: императивная парадигма.
Циклы
22
• Цикл while:
while expr do... // тело цикла
let mutable i = 0while i < 3 do
printfn "%i" ii <- i + 1
Выведет:012
F#: императивная парадигма.
Циклы
23
• Объявление и инициализация очень похожи на работу со списками:
let names = [|"Mary"; "Kate"; "Jane"|]Array.init 5 (fun index -> index + 1) // [|1;2;3;4;5|]Array.create 3 "Hello" // [|"Hello"; "Hello"; "Hello"|]Array.zeroCreate 5 // [|0;0;0;0;0|]
Доступ:let kate = names.[1] // kate = "Kate"
Изменение:names.[2] <- "Lucy"
Разделение (slicing):let slice1 = names.[1..] // slice1 = [|"Kate"; "Jane"|]let slice2 = names.[..1] // slice2 = [|"Mary"; "Kate"|]
F#: императивная парадигма.Массивы
24
• Прямоугольные (массив массивов равной размерности):let arr = Array2D.init<int> 2 3 (fun row col -> row+col)arr = [| [|0; 1; 2|];
[|1; 2; 3|]; [|2; 3; 4|] |]
let zero = arr.[0, 0] // zero = 0
• Вложенные (список массивов не обязательно равной размерности):
let jagged = [| for x in 1..3 do yield [|1..x|] |]jagged = [| [|1|];
[|1; 2|]; [|1; 2; 3|] |]
jagged.[0].[0] = 0
F#: императивная парадигма.
Двухмерные массивы
25
• Полезные функции модуля Array:o fold, foldBack, map, filter, rev — аналогичны
функциям модуля List;o append — соединяет первый аргумент со вторым,
возвращает новый массив. Нет сокращенного имени.o fill — заполняет массив от индекса Start до индекса
finish переданным значением value;o iter — применяет переданную функцию ко всем
элементам массива. В отличии от map, не возвращает нового массива;
o length — возвращает длину массива;o sort — возвращает отсортированную копию массива;o sortInPlace — сортирует элементы в массиве.
F#: императивная парадигма.Массивы
26
Список Массив
Неизменяемый, может содержать общие элементы с
другими списками
Константное время доступа к элементам
Совпадение с образцом Возможность произвольного доступа
Поддерживает map и fold Поддерживает map и fold
Линейное время доступа Не может содержать общие элементы с другими массивами
Нет произвольного доступа к элементам — только
последовательный доступ
Нельзя менять размер на лету (только путем копирования
через Array.append)
F#: императивная парадигма.
Сравнение списков и массивов
27
• Неявный способ определения:
type TypeName optional-type-arguments arguments [ as ident ] = [ inherit type { as base } ] [ let-binding | let-rec bindings ] * [ do-statement ] * [ abstract-binding | member-binding | interface-implementation ] *
F#: объектная парадигма.Определение классов
28
• Неявный способ определения:
type MyPoint(x : int, y : int) = class let mutable color = Color.Black member this.X = x member this.Y = y member x.Color = color member x.SetColor(c:Color) = color <- c member me.Print = printfn "X = %d Y = %d
Color = %A" me.X me.Y me.Colorend
F#: объектная парадигма.Определение классов
29
• Явный способ определения:
type Point = class val X : int val Y : int val Color : Color new (x,y,c) as this = {X = x; Y = y; Color = c} then printf "Point created at (%d;%d)" this.X
this.Y member me.Print = printfn "X = %d Y = %d Color =
%A" me.X me.Y me.Colorend
F#: объектная парадигма.Определение классов
30
• Абстрактные классы:
[<AbstractClass>]type Shape() = override x.ToString() = sprintf "%s, area = %f"
(x.GetType().Name) (x.Area()) abstract member Draw : unit -> unit abstract member Area : unit -> float
type Circle(radius : float) = inherit Shapemember x.Radius = radiusoverride x.Draw() = printfn "(Circle)”override x.Area() = Math.PI * radius * radius
F#: объектная парадигма.Определение классов
31
• Пример перегрузки конструкторов:
type Line = class val X1:int val X2:int val Y1:int val Y2:int new() = { X1 = 0; X2 = 0; Y1 = 0; Y2 = 0 } new(x1,x2,y1,y2) = { X1 = x1; X2 = x2; Y1 = y1; Y2 = y2 } new(p1 : Point, p2 : Point) = { X1 = p1.X; X2 = p2.X; Y1 = p1.Y; Y2 = p2.Y }end
F#: объектная парадигма.Перегрузка методов
32
• Общее описание инфиксного оператора:let (operator) arg1 arg2 = ...
• Пример: оператор совпадения по шаблонуlet (=~) arg pattern = Regex.IsMatch(arg,
pattern)Использование:let result1 = "cat" =~ "dog" // result1 = falselet result2 = "catamorphism" =~ "cat*"
// result2 = true
F#: объектная парадигма.Перегрузка операторов
33
Последовательности в F#• Последовательность (sequence) — это список,
значение которого вычисляются «лениво», т.е. по требованию.
let numbers = seq {0I .. 1000000000000I}
Или через использование генераторов:
let numbers = seq{for i in 0I..1000000000000I do yield i}let firstTen = Seq.take 10 numbers// firstTen = [0;1;2;3;4;5;6;7;8;9]let million = Seq.nth 1000000 numbers// million = 1000000
34
Асинхронность и параллелизм
• Модуль Async позволяет работать с асинхронными, синхронными и параллельными вызовами функций.
• Пример: простой синхронный веб-краулер
let getLinks url = async { let webClient = new WebClient() printfn "Downloading %s" url let html = webClient.DownloadString(url : string) printfn "Got %d bytes" html.Length let matches = Regex.Matches(html, @"http://\S+") printfn "Got %d links" matches.Count return url, matches.Count }let result = Async.RunSynchronously(getLinks "http://www.msn.com/")
35
• Пример: простой асинхронный веб-краулер
let extractLinksAsync html = async { return System.Text.RegularExpressions.Regex.Matches(html,
@"http://\S+") }
let getLinks url = async { let webClient = new System.Net.WebClient() let html = webClient.DownloadString(url : string) let! links = extractLinksAsync html return url, links.Count }let links = getLinks "http://www.msn.com/"let result = Async.Run links
Асинхронность и параллелизм
36
• Пример: простой параллельный асинхронный веб-краулер
let extractLinks html = Regex.Matches(html, @"http://\S+")
let getLinks url = let webClient = new System.Net.WebClient() let html = webClient.DownloadString(url : string) extractLinks html
let download url = let links = (getLinks url) url, links.Count
let urls = [@"http://www.msn.com/"; @"http://www.microsoft.com/"; @"http://www.techdays.ru/"]
let result = seq { for u in urls -> async { return download u } } |> Async.Parallel |> Async.RunSynchronously
Асинхронность и параллелизм
37
Ресурсы о F#• Microsoft F# Developer Center:
http://msdn.microsoft.com/ru-ru/fsharp/default(en-us).aspx
• Wikibooks: F# Programming: http://en.wikibooks.org/wiki/F_Sharp_Programming
• Don Syme's WebLog on F# and Related Topics: http://blogs.msdn.com/dsyme/
• F# For Scientists: http://www.amazon.com/F-Scientists-Jon-Harrop/dp/0470242116
• Beginning F#: http://apress.com/book/view/1430223898
• Expert F#: http://apress.com/book/view/1590598504
38
?