38
Функциональное программирование на F# Богомолов Юрий [email protected] 1

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

Embed Size (px)

DESCRIPTION

Функциональное программирование на F# / DevCamp Винница.

Citation preview

Page 1: Функциональное программирование на F#

1

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

е на F#

Богомолов Юрий[email protected]

Page 2: Функциональное программирование на F#

2

Общие сведенияF# — мультипарадигмальный язык:• Функциональное программирование:

o Строго типизированный язык с возможностью выведения типов;

o Поддерживает каррирование функций, лямбда-функции, замыкания, композицию функций;

o Использование совпадений с образцом (pattern-matching);

o Алгебраические типы данных (discriminated unions).

Page 3: Функциональное программирование на F#

3

Общие сведенияF# — мультипарадигмальный язык:• Императивное программирование:

o Изменяемые данные;o Управление потоком выполнения (условные

переходы, циклы);o Изменяемые массивы, списки и словари;o Возможность использования изменяемых

переменных.

Page 4: Функциональное программирование на F#

4

Общие сведенияF# — мультипарадигмальный язык:• Объектно-ориентированное

программирование:o Поддержка пользовательских типов данных

(записи, кортежи, классы);o Классы:

• Наследование;• Перегрузка операторов;

o Последовательные, параллельные, асинхронные вычисления.

Page 5: Функциональное программирование на F#

5

Типы данныхТип данных Название

Целочисленныйsbyte, byte, int16, uint16, int/int32,

uint32, int64, uint64, bigint

С плавающей точкой float32, float, decimal

Строковый char, string

Логический bool

Специальныеunit, кортежи,

последовательности, списки, размеченные объединения

Page 6: Функциональное программирование на F#

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#: функциональная парадигма

Page 7: Функциональное программирование на 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#: функциональная парадигма

Page 8: Функциональное программирование на 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#: функциональная парадигма

Page 9: Функциональное программирование на F#

• Функция, имеющая тело, но не имеющая имени.

• Аналог из C# — анонимные делегаты.• Синтаксис:(fun arg1 arg2 ... -> expr)let x = (fun x -> x * x) 2 // возвратит 4

9

F#: функциональная парадигма.

Лямбда-функции

Page 10: Функциональное программирование на 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#: функциональная парадигма.

Совпадение с образцом

Page 11: Функциональное программирование на 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#: функциональная парадигма.

Совпадение с образцом

Page 12: Функциональное программирование на 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#: функциональная парадигма.

Работа со списками

Page 13: Функциональное программирование на 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#: функциональная парадигма.

Работа со списками

Page 14: Функциональное программирование на 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#: функциональная парадигма.

Работа со списками

Page 15: Функциональное программирование на F#

15

• Являются алгебраическим типом данных.• Представляют собой определенный набор

вариантов выбора. Дают возможность выявить ошибки на этапе компиляции.

• Синтаксис:

type unionName =| Case1| Case2 as datatype| …

F#: функциональная парадигма.

Размеченные объединения

Page 16: Функциональное программирование на 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#: функциональная парадигма.

Размеченные объединения

Page 17: Функциональное программирование на 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#: функциональная парадигма.

Размеченные объединения

Page 18: Функциональное программирование на 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#: императивная парадигма

Page 19: Функциональное программирование на 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#: императивная парадигма

Page 20: Функциональное программирование на 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#: императивная парадигма.

Циклы

Page 21: Функциональное программирование на 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#: императивная парадигма.

Циклы

Page 22: Функциональное программирование на F#

22

• Цикл while:

while expr do... // тело цикла

let mutable i = 0while i < 3 do

printfn "%i" ii <- i + 1

Выведет:012

F#: императивная парадигма.

Циклы

Page 23: Функциональное программирование на 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#: императивная парадигма.Массивы

Page 24: Функциональное программирование на 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#: императивная парадигма.

Двухмерные массивы

Page 25: Функциональное программирование на 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#: императивная парадигма.Массивы

Page 26: Функциональное программирование на F#

26

Список Массив

Неизменяемый, может содержать общие элементы с

другими списками

Константное время доступа к элементам

Совпадение с образцом Возможность произвольного доступа

Поддерживает map и fold Поддерживает map и fold

Линейное время доступа Не может содержать общие элементы с другими массивами

Нет произвольного доступа к элементам — только

последовательный доступ

Нельзя менять размер на лету (только путем копирования

через Array.append)

F#: императивная парадигма.

Сравнение списков и массивов

Page 27: Функциональное программирование на 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#: объектная парадигма.Определение классов

Page 28: Функциональное программирование на 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#: объектная парадигма.Определение классов

Page 29: Функциональное программирование на 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#: объектная парадигма.Определение классов

Page 30: Функциональное программирование на 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#: объектная парадигма.Определение классов

Page 31: Функциональное программирование на 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#: объектная парадигма.Перегрузка методов

Page 32: Функциональное программирование на 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#: объектная парадигма.Перегрузка операторов

Page 33: Функциональное программирование на 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

Page 34: Функциональное программирование на F#

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/")

Page 35: Функциональное программирование на F#

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

Асинхронность и параллелизм

Page 36: Функциональное программирование на F#

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

Асинхронность и параллелизм

Page 38: Функциональное программирование на F#

38

?