{"id":15060864,"url":"https://github.com/yashelter/fsharp-learning","last_synced_at":"2025-04-10T06:12:17.471Z","repository":{"id":226916704,"uuid":"769910719","full_name":"yashelter/fsharp-learning","owner":"yashelter","description":"Тут можно найти учебный материал и решённые задачи по программированию на F# :)","archived":false,"fork":false,"pushed_at":"2024-07-02T09:32:43.000Z","size":28504,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T07:22:03.927Z","etag":null,"topics":["fsharp","fsharp-exercises","fsharp-practice","fsharp-samples"],"latest_commit_sha":null,"homepage":"","language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yashelter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-10T12:13:52.000Z","updated_at":"2024-10-08T18:10:53.000Z","dependencies_parsed_at":"2024-03-15T14:47:26.779Z","dependency_job_id":"17bd800f-b8d7-45bd-837b-567740d7ff7d","html_url":"https://github.com/yashelter/fsharp-learning","commit_stats":null,"previous_names":["yashelter/fsharp-learning"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yashelter%2Ffsharp-learning","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yashelter%2Ffsharp-learning/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yashelter%2Ffsharp-learning/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yashelter%2Ffsharp-learning/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yashelter","download_url":"https://codeload.github.com/yashelter/fsharp-learning/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248166925,"owners_count":21058481,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fsharp","fsharp-exercises","fsharp-practice","fsharp-samples"],"created_at":"2024-09-24T23:05:33.552Z","updated_at":"2025-04-10T06:12:17.449Z","avatar_url":"https://github.com/yashelter.png","language":"F#","readme":"# О репозитории и авторах \nЭто конспект составленный [мной](https://github.com/yashelter) во время прохождения институтского курса функциононального программирования. Также тут можно найти работу \u003ci\u003e\u003cb\u003e[по компилятору на F#](/fp-compiler-lab-starlight-sky/)\u003c/i\u003e\u003c/b\u003e, к которому также приложили руку [ZBelka100](https://github.com/ZBelka100) и [MaximusPokeZ](https://github.com/MaximusPokeZ).\nЕсли у вас есть вопросы или предложения можете смело писать их в issues или мне лично в [![Telegram](https://img.shields.io/badge/telegram-%231DA1F2.svg?\u0026logo=telegram)](https://t.me/yashelter)\n\nЕсли вам помог мой скромный труд, то пожалуйста поставьте звёздочку, мне будет приятно\n![Alt Text](/staff/yu.gif)\n## Небольшое оглавление: \n- [О репозитории и авторах](#о-репозитории-и-авторах)\n  - [Небольшое оглавление:](#небольшое-оглавление)\n    - [Полезные ссылки /0](#полезные-ссылки-0)\n    - [Основы F# /1](#основы-f-1)\n        - [Выводы по главе \\\\1:](#выводы-по-главе-1)\n    - [Функции, и их применение /2](#функции-и-их-применение-2)\n      - [fun vs. function:](#fun-vs-function)\n      - [Рекурсия](#рекурсия)\n    - [Списки, алгебраические типы \\\\3](#списки-алгебраические-типы-3)\n        - [Некоторые функции списков](#некоторые-функции-списков)\n      - [Немного примеров](#немного-примеров)\n      - [Чуть подробнее про fold и foldBack](#чуть-подробнее-про-fold-и-foldback)\n    - [Различные структуры \\\\4](#различные-структуры-4)\n      - [Дерево общего вида](#дерево-общего-вида)\n      - [Очереди](#очереди)\n      - [Zippers](#zippers)\n    - [Приёмы функционального программирования \\\\5](#приёмы-функционального-программирования-5)\n      - [Замыкания](#замыкания)\n      - [Примеры задач с mutable и замыканием](#примеры-задач-с-mutable-и-замыканием)\n      - [Генераторы](#генераторы)\n      - [Последовательности](#последовательности)\n      - [Продолжения](#продолжения)\n      - [Мемоизация](#мемоизация)\n    - [Монады и монадические выражения \\\\6](#монады-и-монадические-выражения-6)\n      - [Сперва примеры](#сперва-примеры)\n      - [Определение монад](#определение-монад)\n      - [Монадические законы](#монадические-законы)\n      - [Монадические выражения](#монадические-выражения)\n      - [Теория категорий, типизация и функциональное программирование](#теория-категорий-типизация-и-функциональное-программирование)\n        - [Категории](#категории)\n      - [Функторы](#функторы)\n        - [Категориальное определение пары](#категориальное-определение-пары)\n      - [Снова Монады](#снова-монады)\n      - [Оптика](#оптика)\n\n### Полезные ссылки /0\n+ [Лямбда-исчисление](https://neerc.ifmo.ru/wiki/index.php?title=Лямбда-исчисление#.D0.9B.D1.8F.D0.BC.D0.B1.D0.B4.D0.B0-.D0.B8.D1.81.D1.87.D0.B8.D1.81.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5)\n+ [Применение функторов и монад](https://habr.com/ru/articles/686768/)\n+ [Документация F#](https://learn.microsoft.com/en-us/dotnet/fsharp/)\n\n\n### Основы F# /1\n\nЧто такое F#:\n- Functional first programming\n- Взаимодействие с .NET (C#)\n- Разрабатывать можно практически всё\n\n+ Функциональное программирование - стиль, который ближе к математике\n+ Функциональные программы проще распараллеливать\n+ В чисто функциональных программах нет присваивания и изменения переменных, а значит и меньше побочных эффектов.  \n\nПростые примеры:\n\nУмножение на 2\n```fsharp\nlet twice x = x * 2\ntwice 3\n```\n\nВторой вариант того же самого\n```fsharp\n(fun x -\u003e x * 2) 3\n```\n\nДанные варианты эквиваленты:\n```fsharp\nlet f x = x * 2\nlet f = (fun x -\u003e x * 2)\n```\n\nРешение квадратных уравнений\n```fsharp\nlet solve (a, b, c) =\n    let d = b * b - 4. * a * c\n    let x1 = (-b - sqrt (d)) / (2. * a)\n    let x2 = (-b + sqrt (d)) / (2. * a)\n    (x1, x2)\n\nsolve \u003c| (1., 2., 1.)\nsolve \u003c| (1., 2., -3.)\n```\n\nВ функциональном программировании функции нескольких аргументов сводятся к функции одного аргумента. \n\nКак работает несколько аргументов (Карирование):\n\n```fsharp\nlet plus x y = x + y;\nlet pre = (plus 1)\npre 2\n```\nТут наглядно видно что технически мы не применяем аргументы, а сначала создаём функцию которая подставляет на место первого 1, а затем можно подставить другое число. Те можно понять как частичное выполнение функций, или подстановку в макрос. Также можно представить excel таблицу, где формулы к ячейкам применяются аналогично.\n\n```fsharp\n// если представить как лямбда выраждение становится очевидным как работает карирование\nlet plus = fun x -\u003e (fun y -\u003e x + y)\n```\n\u003e Если в функцию передавать один аргумент, то она уже не карируется, а выполняется, а кортеж является одним элементом\n\nИтого:\n- В функциональном программировании функции нескольких аргументов сводятся к функции одного аргумента\n- Каррированая форма позволяет частичное применение функции\n\nУсловный оператор:\n\n```fsharp\nlet max x y = if x \u003e y then x else y\n\n// если значение не возвращается (unit), то можно опустить else\nlet greater x y = if x \u003e y then printfn \"Truth\"\n```\n\nПередача функции в качестве параметра\n```fsharp\nlet table f =\n    printfn \" X     f(X)\"\n    for i = 0 to 10 do\n        let x = 1./float(i)\n        printfn \"%7.4f    %7.4f\" x(f, x)\n```\n\nЕщё интересные пример:\n\n```fsharp\nlet min a b = if a \u003e b then b else a\nlet max a b = if a \u003e b then a else b\n\nlet triple f a b c = f (f a b) c\ntriple (+) 1 2 3 // 6\ntriple min 8 9 3 // 3\n```\n##### Выводы по главе \\1:\n- Функциональные программы очень лаконичны. В том числе из-за вывода типов.\n- Все переменные - неизменяемые.\n- Есть две базовые операции - определение функции и её применение. На их основе всё построено.\n- Функции можно легко передавать в качестве аргументов в другие функции (функции высших порядков).\n- Функции нескольких аргументов сводятся к функциям одного аргумента с помощью каррирования.\n- В нем нет изменяемых переменных, поэтому нет побочных эффектов проще распараллеливать код\n- Основная мощь - возможность композиции функций, работа с функциями как с данными\n- Краткая запись (вывод типов)\n\n### Функции, и их применение /2\n\nСначала введём основные понятия (выдадим базы)\n\n**Абстракция** - создание функциональной константы из выражения\n```fsharp\n// тут нам по сути не важно как мы назовём x, а это лишь идея\n// что это функция которая прибавит 1\nfun x -\u003e x + 1 \n```\n**Аппликация** - вызов функции с некоторыми данными\n```fsharp\nsin 0.5\n```\n\n**Редукция** — упрощение выражений в ходе вычислений (подстановка)\n\nРедукция аппликации абстракции называется Beta-редукцией (вместо x подставить что-то)\n\nРедукция специальных функций называется Delta-редукцией (замена функции)\n\n\u003cbr\u003e\nВ F# энергичный (аппликативный порядок редукции) \nПри том, аппликативная редукция не всегда приводит к результату, а может приводить к зацикливанию.\n\n```fsharp\n(fun x -\u003e x * 2) (2*3) // мы посчитаем сначала x=6\n// в нормальном (ленивом ((не F#)))  мы сначала подставим x=2*3, \n// а лишь в конце будем всё вычислять\n```\n\n**Комбинатор** — базовая функциональная единица, выражение без свободных переменных\n\n```fsharp\n// комбинаторы: \nlet I x = x // единичный\nlet K x y = x // канцелятор\n\nlet S x y z = x z (y z) // распределитель\nlet flip f x y = f y x // смена порядка\n```\n\nКомпозиция:\n\n```fsharp\n// как они внутри\nlet (\u003e\u003e) f g = fun x -\u003e g(f x)\nlet (\u003c\u003c) f g = fun x -\u003e f(g x)\n\nlet (|\u003e) x f = f x\nlet (\u003e|) f x = f x\n```\n\nПример\n\n```fsharp\n//Комбинатор\nlet combine f g x = f (g x) // \u003c\u003c\n\nlet addOne x = x + 1\nlet double x = x * 2\n\nlet addOneThenDouble = combine double addOne\n\nprintfn \"%d\" (addOneThenDouble 3)\n\n// все эквивалентны \nlet f x = 2 * x + 1\nlet f = fun x -\u003e 2 * x + 1\n\nlet f = fun x -\u003e x |\u003e (*) 2 |\u003e (+) 1\nlet f = (*) 2 \u003e\u003e (+) 1\n```\n\nСистема типов параметрический полиморфизм.\n\n```fsharp\n// прямая сумма int + string\ntype PesonAge =\n    | Exact of int\n    | Desc of string\n\nlet student = Exact(12)\nlet name = Desc(\"Ivan\")\n\n```\n\nOption тип возвращает либо значение, если оно получено, либо None. Другими словами, это тип, который в зависимости от условий может принимать различные подтипы.\n\n```fsharp\n// уже в языке\ntype 'T option =\n    | Some of 'T\n    | None\n\n// эквивалентно\ntype option\u003c'T\u003e =\n    | Some of 'T\n    | None\n```\n\nУдобно создавать свои типы, что бы обрабатывать выходные значения функций.\n\n```fsharp\ntype SolveResult =\n    | None\n    | Linear of float\n    | Quadratic of float * float\n\nlet solve a b c =\n    let D = b * b - 4. * a * c\n    if a = 0. then\n        if b = 0. then None \n        else Linear(-c / b)\n    else if D \u003c 0. then\n        None\n    else\n        Quadratic(((-b + sqrt (D)) / (2. * a), (-b - sqrt (D)) / (2. * a)))\n\nlet x = solve 1. 2. -3.\n```\n\nФормальные выводы:\n- Статическая типизация лучше динамической, т.к. позволяет дополнительно проверить правильность программы на этапе компиляции.\n- В функциональных языках богатая система типов, включая функц. типы, полиморфизм и дизьюнктное объединение\n- Система типов позволяет хорошо моделировать предметную область.\n\nСопоставление с образцом (например делается с помощью оператора match (что-то) with (список того-то)):\n\n```fsharp\nlet print r =\n    match r with\n        | None -\u003e printfn \"No Solutions\"\n        | Linear(x) -\u003e printfn \"x=%f\" x\n        | Quadratic(x1, x2) -\u003e printfn \"x1=%f, x2=%f\" x1 x2\n\nprint x // или можно так solve 1. 2. -3. |\u003e print \n```\n\nСледует обратить внимание в примере выше, на параметры в скобках, те мы по факту явно указываем чего ожидаем от `r`\n\nОднако помимо типа, можно указать и условие, а также выбрать остальные результаты (else) с помощью `_`\n```fsharp\nlet print r =\n    match r with\n        | None -\u003e printfn \"No Solutions\"\n        | Linear(x) -\u003e printfn \"x=%f\" x\n        | Quadratic(x1, x2) when x1=x2 -\u003e printfn \"x1=x2=%f\" x1 // условие\n        | Quadratic(x1, x2) -\u003e printfn \"x1=%f, x2=%f\" x1 x2\n        | _ -\u003e printfn \"Not expected?\" // пример else\n\nprint x // или можно так solve 1. 2. -3. |\u003e print \n```\n\nОднако не обязательно явно использовать оператор match, можно сделать тоже самое с помощью function:\n```fsharp\nlet print = function\n        | Linear(x) -\u003e printfn \"x=%f\" x\n        | Quadratic(x1, x2) when x1=x2-\u003e printfn \"x1=x2=%f\" x1\n        | Quadratic(x1, x2) -\u003e printfn \"x1=%f, x2=%f\" x1 x2\n        | _ -\u003e printfn \"Not expected?\"\n\nprint x\n```\nОт сюда возникает вопрос в отличии задания анонимных функций с помощью `function` и `fun`\n\n#### fun vs. function:\n\n`fun`\n+ Поддерживает несколько аргументов в каррированной форме: fun xу-\u003e ...\n+ Не поддерживает pattern matching function\n\n`function`\n+ Поддерживает только один аргумент (например tuple)\n+ Поддерживает pattern matching с несколькими вариантами описания\n\nПример сопоставления пар:\n```fsharp\nlet order = function\n    | (x1, x2) when x2\u003ex1 -\u003e (x2, x1)\n    | _ as pair -\u003e pair\n\norder (2, 4)\n```\n\n```fsharp\nlet x1, x2 = 'u', 'v' // попарное присвоение \n\n// пример реализации встроенных fst и snd\n// также работают через сопоставление с образцом\nlet fst (x1, x2) = x1 \nlet snd (x1, x2) = x2\n```\n\nИ весь смысл pattern matching, что удобнее чем условный оператор, и часто применяется неявно\n\n#### Рекурсия\n\nПонятие рекурсии эквивалентно её понятию в императивных языках. И рекурсия считается плохой, если она не хвостовая, которая может быть преобразована компилятором в цикл.\n\nПримеры рекурсии:\n\n```fsharp\n// обычная рекурсия\nlet rec factorial n = \n    if n=1 then 1\n    else n*factorial (n-1)\n\nfactorial 5\n```\n\n```fsharp\n// обычная с сопостовлением\nlet rec factorial = function\n    | 1 -\u003e 1\n    | n -\u003e n * factorial(n-1)\n\nfactorial 5\n```\n\n```fsharp\n// пример хвостовой рекурсии\nlet rec factorial n acc = \n    // появлеяется acc - аккумулятор результата\n    if n=1 then acc\n    else factorial (n-1) (acc * n)\n\nfactorial 5 1\n```\n\n\n### Списки, алгебраические типы \\3\n\nВ функциональных языках нет, и не может быть массивов (тк это область памяти, а у нас тут все переменные не изменяемые), поэтому вместо них используются списки\n\n\u003e Тут можно возразить, тк в F# всё же есть массивы `[||]`, но это *императивный* элемент, а не *функциональный*. \n\n\u003c/br\u003e\nСписок в f# похож на список prolog'a (Elem, (Tail)), где Tail продолжение списка (другой список, возможно пустой `Nil`)\n\nПример списка и функции поиска его длины\n```fsharp\n//type 't list = Nil | Cons of 't * 't list // можно так\ntype list\u003c't\u003e = Nil | Cons of 't * list\u003c't\u003e // или так\n\nlet empty = Nil\nlet otf = Cons(1, Cons(2, Cons(3, Nil)))\n\n// при том такой тип решения самый хороший\nlet rec get_length (acc:int) = function\n  | Nil -\u003e acc\n  | Cons(_, tail) -\u003e get_length  (acc + 1) tail\n\nget_length 0 otf  // 3\n\n```\n\n```fsharp\n// Стандартное обозначение списка на F#\ntype 'a list = \n    | ([])\n    | (::) of 'a * 'a list\n```\n```fsharp\n// задание списка пример\nlet squares = [   for i in 1 .. 20 do\n                    if i % 5 = 0 then\n                        yield i]\n```\n\nХвост списка можно отделять с помощью оператора `Head_Lst::Tail_Lst` (либо вставлять)\n\n\nСвёртка (fold) в F# - это функция высшего порядка, которая применяет некоторую операцию к элементам списка, последовательно сворачивая его до одного значения. Эта операция может быть суммированием, умножением, конкатенацией или любой другой операцией, определенной пользователем.\n\n```fsharp\nval List.fold : ('State -\u003e 'T -\u003e 'State) -\u003e 'State -\u003e 'T list -\u003e 'State\n```\n\n```fsharp\n// fold example\nlet rec fold_ f i = function\n    | [] -\u003e i\n    | h::t -\u003e f (fold_ f i t) h\n    | _ as list -\u003e \n        let t = List.tail list\n        let h = List.head list\n        f (fold_ f i t) h\n\nfold_ (+) 0 [1..100]\n```\n\nАналогом является Reduce, которая по факту такая же, но без использования начального значения.\n\n```fsharp\n// reduce example\nlet rec reduce_ f = function\n    | [] -\u003e failwith \"Error\"\n    | [x] -\u003e x\n    | h::t -\u003e f (reduce_  f t) h\n\nreduce_ (+) [1..100]\n```\n\nНадо стараться при обходе списков использовать хвостовую рекурсию(и вообще всегда), тк она не требует дополнительного расхода памяти и может быть оптимизирована компилятором (тк по факту не требуются возвраты к начальному контексту)\n\n```fsharp\n// пример такого метода fold\nlet lst = [1..100]\n\nlet fold f i lstt = \n    let rec fold' acc = function\n        | [] -\u003e acc\n        | h::t -\u003e fold' (f h acc) t\n    fold' i lstt\n    \nlet result = fold (+) 0 lst\nprintfn \"%d\" result\n```\n\nКонкатенация списков\n```fsharp\nlet f x acc = x::acc\n\nlet filteredList = List.foldBack f [1..10] [11..20] \n\nprintfn \"%A\" filteredList\n\n// или\nlet a = [1..10]\nlet b = [11..2..180]\n\nlet rec concat lst1 = function\n    |[] -\u003e lst1\n    |h::t -\u003e concat (lst1@[h]) t\n    \nprintfn \"%A\" (concat a b)\n\n```\n\n##### Некоторые функции списков\n\n| Функция | Действие|\n|-------------|-------------|\n|  `List.empty `  | Возвращает пустой список    |\n| `List.isEmpty`  | Проверяет на пустоту    |\n| `List.head `   | Первый элемент списка    |\n| ` List.tail `   | Список без первого элемента    |\n| ` List.length `   | Длина списка    |\n| ` List.exist `   | Есть ли элемент, который пройдёт условие    |\n| ` List.rev `   | Реверсирование списка   |\n| ` List.zip `   | Принимает 2 списка одной длины, и делает список кортежей    |\n| ` List.filter `   | Новый список по старому, где выполнено условие |\n| `List.partition`   | Два списка из исходного. Первый, где функция `true`, второй где `false`    |\n| ` List.map`   | Применяет функцию к каждому элементу списка   |\n| ` List.mapi`   | Применяет функцию к каждому элементу списка, у функции как параметр есть индекс   |\n| ` List.init `   | Задание списка c длиной и функцией от индекса    |\n| ` List.allpairs `   | Декартового произведение двух списков   |\n\nСпособы задания списков\n```fsharp\nList.init 9 (fun x-\u003e2.*float(x))\n[for x in 0..8 -\u003e 2.*float (x)]\n[1..100]\n[1..2..100]\n[ for x in 1..10 do if x%3=0 then yield x]\n```\n#### Немного примеров\n\nПример реализации вычисление среднего арифметического значения в списке\n```fsharp\nlet lst = [1..100]\n\nlet f acc elem = (fst acc + elem, snd acc + 1)\n\nlet getAverage L = \n    let (n, cnt) = List.fold f (0, 0) L\n    float n / float cnt\n\ngetAverage lst\n```\n\nТранспонирование матрицы (списка)\n```fsharp\nlet rec slice = function\n    | [] -\u003e ([], [])\n    | h::t -\u003e\n        let (u, v) = slice t\n        (List.head h ::u, List.tail h ::v)\n\nslice [[1;2;3];[4;5;6]]\n\n\nlet rec transpon = function\n| []::_  -\u003e []\n| x  -\u003e\n    let (h, r) = slice x\n    h::(transpon r)\n\ntranspon [[1;2;3];[4;5;6]]\n```\n\nСортировка списка выбором:\n\n```fsharp\nlet rec fmin = function\n    | [] -\u003e failwith \"unexpected\"\n    | [x] -\u003e (x, [])\n    | h::t -\u003e \n        let (h1, t1) = fmin t\n        if h\u003ch1 then (h, t)\n        else (h1,h::t1)\n\nlet rec sort = function\n    | [] -\u003e []\n    | lst -\u003e\n        let (elem, other) = (fmin lst)\n        elem::(sort other)\n\nsort [1;4;6;7;12;13452;23;23;3]\n```\n\nЗадача на составление списка простых чисел\n\n```fsharp\n// решето эратосфена\nlet lst = [2..100]\n\nlet rec simple = function\n    | []  -\u003e []\n    | h::t -\u003e h::simple(List.filter (fun x -\u003e x % h\u003c\u003e0) t)\n    \nsimple lst\n```\n\u003cdetails\u003e\n  \u003csummary\u003eЗадача на поиск степеней двойки\u003c/summary\u003e\n  \n  Во время тестирования решета, был обноружен интересный способ поиска списка степеней двойки\n  \n  ```fsharp\n  let lst = [2..10000] // обязательно с 2х начало!\n\n  let rec simple = function\n    | []  -\u003e []\n    | h::t -\u003e h::simple(List.filter (fun x -\u003e x % h=0) t) // =\n            \n  simple lst\n  ```\n  Решение пожалуй не самое наверное очевидное, но его можно доказать\n\u003c/details\u003e\n\n#### Чуть подробнее про fold и foldBack\n```fsharp\n// правая свёртка List.foldBack\nlet rec foldr f i = function\n    | [] -\u003e i\n    | h::t -\u003e f h (foldr f i t)\n\n// левая свёртка List.fold\nlet rec foldl f i = function\n    | [] -\u003e i\n    | h::t -\u003e foldl f t (f i h)\n```\nПравая:\n+ Обходит список справа налево\n+ Нехвостовая рекурсия\n\nЛевая:\n+ Обходит список слева направо\n+ Соответствует итерации\n+ Хвостовая рекурсия\n+ Более эффективная\n\n### Различные структуры \\4\n\n#### Дерево общего вида\nДерево общего вида типа T это - \n+ Элемент типа Т с присоединёнными к нему деревьями типа Т\n+ Формально связанный граф без циклов\n+ Пустого дерева общего вида не существует\n\nОписание задания простого дерева\n```fsharp\ntype 'T tree =\n    | Leaf of 'T\n    | Node of 'T * ('T tree list)\n\nlet tr = Node(1, \n            [Node(2,\n                [Leaf(5)]);\n            Node(3,[\n                Leaf(6);\n                Leaf(7)]);\n            Leaf(4)])\n```\n\nСвёртка и отображение (map), достаточно тривиальны:\n\n```fsharp\n// отображение\nlet rec map f = function\n    | Leaf(x) -\u003e Leaf(f x)\n    | Node(x, l) -\u003e\n        Node (f x,  List.map (map f ) l)\n\n// свёртка\nlet rec fold f i = function\n    | Leaf(x) -\u003e f i x\n    | Node(x, l) -\u003e \n        List.fold (fold f) (f i x) l\n```\n\nАбстрактное синтаксическое дерево. Это максимально похоже на задание курсовой\n```fsharp\ntype 't Expr =\n    | Add of 't Expr * 't Expr\n    | Sub of Expr\u003c't\u003e * Expr\u003c't\u003e\n    | Neg of 't Expr\n    | Value of 't\n\nlet rec compute = function\n    | Add(x,y) -\u003e compute x + compute y\n    | Sub(x,y) -\u003e compute x - compute y\n    | Neg(x) -\u003e - compute x \n    | Value(x) -\u003e x\n\ncompute(Add(Neg(Value(5)), Sub(Value(5),Value(1)))) // -5 + 5 - 1 = -1\n```\n\nТакже существуют двоичные деревья. Любое дерево общего вида может быть преобразовано к двоичному\n\n#### Очереди\n\nРеализация наивной очереди\n\n```fsharp\ntype 'a sillyqueue = 'a list\n\nlet rec put x = function\n    | [] -\u003e [x]\n    | h::t -\u003e h::(put x t)\n\nlet take = function\n    | [] -\u003e failwith \"empty\"\n    | h::t -\u003e (h, t)\n\nlet emptyq = []\n```\n\nУмная очередь - храним два списка, добавляем во второй, берём из первого, когда элементы в первом заканчиваются, перестраиваем его, копированием обратного второго:\n\n```fsharp\ntype 'a queue = 'a list * 'a list\n\nlet tail (L,R) = \n    match L with\n    | [x] -\u003e (List.rev R, [])\n    | h::t -\u003e (t, R)\n\nlet head (h::_,_) = hash\n\nlet put x (L, R) =\n    match L with\n    | [] -\u003e ([x], R)\n    | _ -\u003e (L, x::R)\n\nlet empty = ([],[])\n```\n\n#### Zippers\nZippers - аналог двунаправленных списков, где мы смортим на 1 элемент и имеем опрерации сдвигов.\n\n```fsharp\ntype 't ZipperList = 't list * 't list\n\nlet zip l :ZipperList\u003c't\u003e = ([], l)\nlet unzip (l,r) = (List.rev l) @ r\n\nlet right (l, h::t) = (h::l, t)\nlet left (h::t, r) = (t, h::r)\nlet update x (l, h::r) = (l, x::r)\nlet insert x (l,r) = (l, x::r)\n\nzip [1..3] |\u003e right |\u003e update 0 |\u003e unzip\n```\n\nZipper для дерева\n\nЭвристики:\n+ текущий элемент определяется путём до вершины\n+ чтобы полностью определить дерево с текущим элементом, нужно хранить текущее поддерево + путь до него со всеми элементами для восстановления дерева\n    - идя направо запоминаем левое поддерево\n    - идя налево - правое\n+ каждый шаг выворачивает дерево\n\n\n```fsharp\ntype 'T tree =\n    | Leaf of 'T\n    | Node of 'T * 'T tree * 'T tree\n\nlet sample = Node ('+',  \n    Node('*', Leaf('1'), Leaf('2')), \n    Node('*', Node('+', Leaf('3'), Leaf('4')), Leaf('5')))\n\ntype 't crumb = \n    | Left of 't * 't tree \n    | Right of 't * 't tree\n\ntype 't  TreeZipper = 't tree * 't crumb list\n\nlet zip t : 't TreeZipper = (t,[])\n\nlet left (Node(x,l,r), path) = (l, Left(x,r)::path)\nlet right (Node(x,l,r), path) = (r, Right(x, l)::path)\n\nlet update value (current, path)  =\n    match current with \n    | Node(x, left, right) -\u003e (Node(value, left, right), path)\n    | Leaf(x) -\u003e (Leaf(value), path)\n\nlet up (t, p) =\n    match p with\n    | Left (x,r)::xs -\u003e (Node(x,t,r), xs)\n    | Right (x, l)::xs -\u003e (Node(x,l,t), xs)\n\nzip sample |\u003e right |\u003e left |\u003e update '0' |\u003e up |\u003e up\n```\n\nВажное преимущество : Zipper'ы позволяют модифицировать текущий, не перестраивая всей структуры\n\n![Котик](https://media.giphy.com/media/vFKqnCdLPNOKc/giphy.gif)\n\n### Приёмы функционального программирования \\5\n\n#### Замыкания\nЗамыкание - совокупность функции и контекста: значений всех свободных переменных входящих в эту функцию, зафиксированных в момент определения функции\n\n```fsharp\n// вот такой пример для понимания\nlet x = 1\nlet f z = x+z\nf 1\n\nlet x = 2 // но если скомпилить целиком ошибка\nf 1\n```\n\nИ в f# есть динамическое связывание\n```fsharp\n// новое старое слово\nlet mutable x = 1 // теперь тут ссылка\nlet f z = x+z\nf 1\n\nx \u003c- 2 // ! внимание на изменение !\nf 1\n```\n+ Замыкание это внутренние и внешние переменные\n+ Замыкания позволяют сохранять внутри функции некоторое состояние\n+ Изменяемые переменные - штука для изменения состояния (императивный прикол, не функциональный уже считается)\n\n#### Примеры задач с mutable и замыканием\nПодсчёт суммы списка\n```fsharp\n// с mutable\nlet lst = [1..100]\n\nlet mutable res = 0\n\nlet rec f = function\n    | [] -\u003e None\n    | h::t -\u003e \n        res \u003c- (res + h) \n        f t\nf lst\nprintf \"%d\" res\n```\n\nГенератор простых чисел\n```fsharp\nlet lst = [2..100]\n\nlet rec simple = function\n    | [] -\u003e []\n    | h::t -\u003e h::simple(List.filter (fun x -\u003e x % h \u003c\u003e0) t)\n\nlet mutable simpels = simple lst\n\nlet get_simple = function \n    | [] -\u003e 0\n    | h::t -\u003e\n        simpels \u003c- t\n        h\n\nlet f = get_simple simpels\n```\n\n#### Генераторы\nГенераторы - штука, которая (генерирует) последовательность (возможно бесконечную). Те это иное представление списка, хотя при этом абстракция. Это пример ленивых (отложенных вычислений)\n```fsharp\n// генератор общего вида\nlet new_generator fgen init =\n    let mutable x = init\n    fun () -\u003e\n        x \u003c- fgen x;x\n\n// модификация генератора\nlet map f gen = \n    fun() -\u003e f (gen())\n\n// фильтр для генератора\nlet rec filter cond gen =\n    fun() -\u003e\n        let x = gen()\n        if cond x then x\n        else (filter cond gen) ()\n\n// взятие n элементов у генератора\nlet rec take n gen =\n    if n=0 then[]\n    else gen()::take(n-1) gen\n\n\nlet fibgen = new_generator (fun (u, v) -\u003e (u+v,u)) (1, 1)\n\n// example\nfibgen |\u003e map fst |\u003e take 10\n```\n\n#### Последовательности\nПоследовательности - тип данных похожий на список и генератор. (LazyList .Net (но с кэшированием результатов)) Те элементы могут не вычисляться до 1го обращения\n\n```fsharp\n// методы задания\nlet squares = Seq.initInfinite(fun n -\u003e n*n)\nlet squares10 = Seq.init 10 (fun n -\u003en*n)\n\n\nseq {1..100}\nseq {for x in 1..10 -\u003e x*x}\nseq {for x in 1..10 do\n        if x%3=0 then yield x*x}\n\n```\n\n```fsharp\n// способы задать факториал\n\nlet fact n =  [1..n] |\u003e List.reduce (*)\n\nlet fact n = seq {1..n} |\u003e Seq.reduce (*)\n\n// (..) оператор задания последовательности\nlet fact  = (..) 1 \u003e\u003e Seq.reduce (*) \n\nlet fact n = Seq.initInfinite ((+) 1) |\u003e Seq.scan (*) 1 |\u003e Seq.nth n\n```\n\nКорекурсия - определение рекуррентной бесконечной последовательности структурно эквивалентное рекурсии.\n\n```fsharp\n// пример определения корекурсии\nlet rec ones = seq{\n    yield 1;\n    yield! ones;\n}\n\nones |\u003e Seq.take 10 //1 1 1..\n```\n\n```fsharp\n// ещё пример определения корекурсии\nlet rec nat = seq{\n    yield 0\n    yield! Seq.map ((+) 1) nat\n}\n\nnat |\u003e Seq.take 10 // 0..10\n```\n#### Продолжения\nПродолжения - штука позволяющая сводить нелинейную рекурсию к хвостовой\n\n```fsharp\n// пример продолжений для длины списка\nlet len L =\n    let rec len' cont = function\n        | [] -\u003e cont 0\n        | h::t -\u003e len' (fun x -\u003e cont(x+1)) t\n    len' (fun x-\u003ex) L \n// те до конца вычислений мы собираем длинную конструкцию в виде функций\n// а как дошли вычисляем его\n// мы обошлись без аккумулятора и получили хвостовую рекурсию\n```\n#### Мемоизация\nДалее, Мемоизация - запоминание вычесленных значений, для оптимизации. Применимо когда каждому входному -\u003e единственное выходное всегда. \n\nМы жертвуем памятью на скорость. Данный приём иногда применяется в добрых алгоритмах (в том числе на олл проге).\n\n```fsharp\n// вот общая идея\nopen System.Collections.Generic\n\nlet memoize (f: 'a -\u003e 'b) = \n    let t = new Dictionary\u003c'a,'b\u003e() // c# hi\n    fun n -\u003e \n        if t.ContainsKey(n) then t.[n]\n        else let res = f n \n             t.Add(n, res) \n             res\n\n\nlet rec fibFast = \n    memoize(\n    fun n -\u003e if n \u003c 2 then 1\n             else fibFast(n-1) + fibFast(n-2))\n```\n\n### Монады и монадические выражения \\6\n\n\nПо началу может быть не понятно, однако чем больше погружаешься, тем более понятно должно становиться, пока снова не станет непонятно `uwu`:)\n\n#### Сперва примеры\n\n```fsharp\n// общая идея\nopen System\n\nlet read() =\n    printf \"\u003e\"\n    let s = Console.ReadLine()\n    try\n        Some((int)s)\n    with _ -\u003e None\n\nlet bind a f =\n    if a=None then None\n    else f (a.Value)\n\nbind (read()) (fun x -\u003e bind (read()) (fun y -\u003e Some(x+y)))\n```\n\nbind берёт значение a и передаёт его на вход функции f, при этом происходит дополнительная обработка.\n\n```fsharp\n// запись с помощью опратора\nlet (\u003e\u003e=) a f =\n    if a=None then None\n    else f (a.Value)\n\nread() \u003e\u003e= fun x -\u003e read() \u003e\u003e= fun y -\u003e Some(x + y)\n```\n\nДругой пример:\n\n```fsharp\nlet inv x =\n    if x \u003c\u003e 0. then Some(1./ x)\n    else None\n\nlet mmap f = fun x -\u003e Some(f x) // вспомогательная функция для оборачивания в Optional\nread() \u003e\u003e= nmap float \u003e\u003e= inv\n```\n\n#### Определение монад\n+ Монадический тип `M\u003cT\u003e`\n+ Операция return: `T -\u003e M\u003cT\u003e`\n+ Операция `\u003e\u003e=` :` M\u003cA\u003e -\u003e (B -\u003e M\u003cB\u003e) -\u003e M\u003cB\u003e`\n\n#### Монадические законы\n+ `return x \u003e\u003e= f` == `f x` : левая единица\n+ `m \u003e\u003e= return `== `m`  : правая единица\n+ `(m \u003e\u003e=f ) \u003e\u003e= g` == `m \u003e\u003e= (fun x -\u003e f x \u003e\u003e= g)` : ассоциативность\n\n#### Монадические выражения\n\n```fsharp\ntype NondetBuilder() = \n    member b.Return x = [x]\n    member b.Bind(mA, f) = List.collect f mA\n\nlet nondet = new NondetBuilder()\n\nlet r = nondet {\n        let! vas = [14; 15]\n        let! pet = [2 * vas; 3 * vas]\n        let lena = pet + 1\n        return lena\n    }\n```\n\n`let!` показывает что должен сработать оператор Bind, что можно понимать как распаковку объекта\n\n+ Монада (нестрого) - некоторый обрамляющий тип + набор специальных операций.\n+ Монада позволяет описывать в явном виде последовательность некоторых операций, семантику выполнения которых мы можем менять.\n\n\nХороший пример для понимания:\n```fsharp\ntype ResultBuilder() =\n    member _.Bind(x, f) = \n        match x with\n        | Some(v) -\u003e f v\n        | None -\u003e None\n    member _.Return(v) = Some(v)\n\nlet result = ResultBuilder()\n\nlet computation = result {\n    let! x = Some(2)\n    do! if x \u003e 5 then Some () else None\n    let! y = Some(20)\n    return x + y\n}\n\nprintfn \"%A\" computation\n```\nАналог:\n```fsharp\ntype OptionBuilder() =\n    member _.Return(x) = Some x\n    member _.Bind(m, f) = \n        match m with\n        | Some x -\u003e f x\n        | None -\u003e None\n\nlet option = OptionBuilder()\n\n\nlet computation = \n    option.Bind(Some 10, \n        fun a -\u003e option.Bind(Some 20, \n            fun b -\u003eoption.Return(a + b)))\n```\n\n`do!` - делает что то, а значение используется для проверки в цепочке выражения\n\n\nМонада Writer - записывает логи по ходу выполнения\n```fsharp\nlet add x y =\n    writer {\n        do! writer.Tell (sprintf \"Log...%A %A\" x y)\n        return x + y\n    }\n```\n\n```fsharp\n// пример реализации\ntype Writer\u003c't\u003e = 't*string\n\ntype WriterBuilder() = \n    member m.Run (t,s) = (t,s) \n    member m.Tell (s:string) = ((),s)\n    member m.Bind(x,f) =\n        let (v,s:string) = m. \n        Run x let (v',s') = m.Run (f v) \n        (v',s+s')\n    member m.Return x = (x,\"\")\n\nlet writer = new WriterBuilder()\n```\nМонада ввода вывода - записывает логи по ходу выполнения\nМонада состояния - позволяет сохранить состояние, имеет методы get, set\n```fsharp\ntype State \u003c's, 't\u003e = 's -\u003e 't* 's\ntype StateBuilder() = \n    member m.Bind(x, f) = \n        (fun s0 -\u003e \n        let a, s = x s0 \n        f a s)\n    member m.Return a = (fun s -\u003e a, s)\n    member m.Zero() = m.Return()\n\nlet state = new StateBuilder()\n\nlet getState = (fun s -\u003e s, s)\nlet setState s = (fun _ -\u003e (s), s)\n```\n\nМонада продолжений\n```fsharp\ntype ContinuationBuilder() =\n    member this.Bind (m, f) =\n        fun c -\u003e m (fun a -\u003e f a c)\n    member this.Return x = fun k -\u003e k x\n\nlet cont = ContinuationBuilder()\n```\n\nТакже монады можно использовать для параллейных вычислений и парсеров (Fparsec работает схоже (вспоминаем синтаксис))\n\n#### Теория категорий, типизация и функциональное программирование\n+ Взгляд на теорию функционального программирования на основе очень абстрактного математического понятия\n+ Изучает взаимосвязь понятий без привязки к их внутренней структуре\n+ Применяется в: \n    - Топологии\n    - Теоретической физике\n    - Информатике\n\n##### Категории\nОпределение - это семейство объектов Ob(K), и семейство морфизмов (\"стрелок\") Mor(K)\n- $\\forall f : A \\to B, g: B \\to C, \\exists g \\circ f : A \\to C$ (композиция)\n- $\\forall A \\in \\text{Ob}(K), \\exists \\text{Id}_A: A \\to A$ (единичная стрелка)\n- $\\forall f, g, h \\in \\text{Mor}(K): (f \\circ g) \\circ h = f \\circ (g \\circ h)$ (ассоциативность)\n- $\\forall f : A \\to B$ имеет место $f \\circ \\text{Id}_A = \\text{Id}_B \\circ f = f$ (свойство единицы)\n\nПримеры категорий:\n+ Категория из одного объекта\n+ Граф пораждает категории\n+ Отношение порядка пораждает категорию\n+ Категория всех множеств Set (каждый объект множество, \"стрелки\" - функции)\n+ Категория всех типов\\функций ЯП (надо рассматривать возможную незавершимость функций)\n\n**Двойственные категории** - такие категории, что \"стрелки\" повернули наоборот. Удобны для доказательств (по двойственности... ).\n+ $Ob(K) = Ob(K_{duo})$\n+ $\\forall f : A \\rightarrow B \\in \\text{Mor}(K), \\exists \\text{g}: B \\rightarrow A \\in \\text{Mor}(K_{\\text{duo}})\n$\n\nНачальный и терминальный объект\n- $A \\in Ob(K)$ - начальный объект, если $\\forall B \\in Ob(K) \\ \\exists ! f : A \\rightarrow B$ \n    + Из него идут стрелки во все другие объекты\n- $A \\in Ob(K)$ - терминальный объект, если $\\forall B \\in Ob(K) \\ \\exists ! f : B \\rightarrow A$ \n    + из каждого объекта сюда приходит одна стрелка\n\nПример начальные и терминальные объекты в Set:\n+ absurd: void $\\rightarrow$ 't - начальный\n+ unit: `let Unit _ = ()`\n\n#### Функторы\n\n- Функтор $F$ – это отображение двух категорий $K_1$ и $K_2$\n- Функтор должен сохранять свойства композиции\n- $F : Ob(K_1) \\rightarrow Ob(K_2)$\n- $F : Mor(K_1) \\rightarrow Mor(K_2)$\n\nСвойства:\n- $F(Id_A) = Id_{F(A)}$\n- Если $f : A \\rightarrow B$, то $F \\ f : F(A) \\rightarrow F(B)$\n- $F(f \\circ g) = F(f) \\circ F(g)$\n\n\u003cdetails\u003e\n\u003csummary\u003eПримеры некоторых функторов\u003c/summary\u003e\nКонструктор : `fmap: (a-\u003eb) -\u003e (F a -\u003e F b)`\n\nSome: \n```fsharp\nlet fmap f = function \n    | None -\u003e None\n    | Some(x) -\u003e Some(f x)\n```\n\n[]: \n```fsharp\nList.map\n```\n\nДерево типа t: \n```fsharp\nTree Map\n```\n\u003c/br\u003e\n\u003c/details\u003e\n\n\u003c/br\u003e\nФунктор отображающий категорию в саму в себя, называется \u003cu\u003e\u003cb\u003eэндофунктором\u003c/u\u003e\u003c/b\u003e\n\nФунктор Some на F#:\n```fsharp\nlet fmap f = function\n    | None -\u003e None\n    | Some(x) -\u003e Some(f x)\n\nfmap ((+) 1) (Some 10)\n\nlet ($) f x = fmap f x // производит лифтинг?\n(+) 1 $ Some(10) \n\n```\n**Лифтинг** - подъём на уровень функтора `(5 -\u003e 6 : Some(5) -\u003e Some(6))`. Те повышение абстрактности.\n\n**Аппликативные функторы** - функторы, для которых мы рассматриваем исходные фукнкции в пространстве функторного типа\n\n```fsharp\nlet (\u003c*\u003e) f x = // example\n    match f, x with\n        | None, _ -\u003e None\n        | _, None -\u003e None\n        | Some(f), Some(x) -\u003e Some(f x)\n\n(Some (+)) \u003c*\u003e Some(1) \u003c*\u003e Some(2) // Some 3\n```\n\n`Some((+))` - можно записать как функцию `Pure(f)`, которая будет поднимать функции\n\n```fsharp\nlet (\u003c!\u003e) a b =\n// для другой функции на вход, вернёт список, где для каждого элемента применит входную функцию\n    let funct = (fun f -\u003e List.map f b) \n    let lst = List.map funct a // применим для списка a\n    lst |\u003e List.concat \n\nlet res = [(+)1;(+)(-1)] \u003c!\u003e [1;2]\nprintfn \"%A\" res\n\nlet pure f = [f]\npure (+) \u003c!\u003e [-1;-2] \u003c!\u003e [3..4]\n```\n\nЗадача. Надо расставить знаки(+,-,\\*) между числами в списке, и посчитать все возможные значения. `[1;2] -\u003e [1*2;1-2;1+2]`\n\n```fsharp\nlet rec split = function\n    | [x] -\u003e []\n    | h::t -\u003e ([h], t)::(List.map (fun (a, b) -\u003e (h::a, b)) (split t))\n\nsplit([1..10])\n\n// все варианты расстановки знаков между числами в списке\nlet rec values = function\n    | [] -\u003e []\n    | [x] -\u003e [x]\n    | Lst -\u003e \n        let variats = split Lst\n        let res = List.map (fun (l, r) -\u003e [(+);(-);(*)] \u003c!\u003e (values l) \u003c!\u003e (values r)) variants\n        List.distinct (List.concat res)\n\n```\n\n##### Категориальное определение пары\n\n- Произведение $A, B \\in Ob(K)$ это:\n  - Объект $C \\in Ob(K)$\n  - Пара стрелок $\\text{fst}: C \\rightarrow A$, $\\text{snd}: C \\rightarrow B$ (проекции)\n\n- При этом $\\forall D \\in Ob(K)$ и $g_1 : C \\rightarrow A$, $g_2 : C \\rightarrow B$ $\\exists ! f : D \\rightarrow C$\n\n+ В категории Set произведение – это $A \\times B$\n\n*Произведение определяется с точностью до изоморфизма*\n\nАлгебраические типы данных можно определять на основе произведения и суммы. \nНапример\n|Значение|Тип|\n|---|---|\n|1|()|\n|a + b|Either a b = Left a | Right b|\n|a * b|(a, b)|\n|2 = 1 + 1|type Bool = True | False|\n|1 + a|type 't option = None | Some 't|\n|0 * a|(Void, 'a)|\n|a \\* (b + c) = a \\* b +a \\* c| ('a, Left 'b \\| Right 'c) = Left ('a, 'b) \\| Right('a, 'c)|\n|1 + 't * x|list\u003c't\u003e = Nil \\| Const 't * list\u003c't\u003e|\n\n#### Снова Монады\n\u003eМонады это моноид в категории эндофункторов\n\nМонады позволяют описать цепочку операций, где что то может пойти не так\n\n```fsharp\nlet good x = Some(x+1)\nlet bad x = None\n\nlet (\u003e\u003e=) x f = \n    match x with\n        | Some(z) -\u003e f z\n        | None -\u003e None\n\nSome(1) \u003e\u003e= good // Some(2)\nSome(1) \u003e\u003e= bad  \u003e\u003e= good // None\n```\n\nТ.Е. это такая штука, которая за нас обработает данные, на случай если они станут плохими в какой то момент, убирая большую вложенность блоков `if-else`\nМатематически называется категория Клейси(`\u003e=\u003e`(fish operator)), отличие с bind(`\u003e\u003e=`), что на вход уже подаётся обёрнутое значение.\n\nСравнение \n|Название|Функция|\n|---|---|\n|Функтор|`fmap (a-\u003eb) -\u003e F a -\u003e F b`|\n|Аппликативный функтор|`\u003c*\u003e : F (a -\u003e b) -\u003e F a -\u003e F b` \u003cbr\u003e`pure f : (a -\u003e b) -\u003e F(a -\u003e b)`|\n|Монада|`\u003e\u003e= : M a -\u003e (a -\u003e M b) -\u003e M b` \u003cbr\u003e`\u003e=\u003e : (a -\u003e M b) -\u003e (b -\u003e M c) -\u003e (a -\u003e M c)` \u003cbr\u003e `return: a -\u003e M a`|\n\n\u003c/br\u003e\n\n#### Оптика\n**Оптика** - набор функциональных абстракций позволяющих работать со сложными структурами данных\n\n**Линзы** решают задачу выделения конкретного места в какой-то структуре данных\n\nПримеры линз с применением\n```fsharp\ntype Content = \n    | String of string \n    | Body of Content list\n    | Collection of Content list\n    | Paragraph of Content\n    | Header of int * Content\n    | Bold of Content\n    | ItemList of Content\n    | TableRow of Content list\n    | Table of Content list\n\nlet doc = Body([\n    Header(1, String \"title\");\n    Paragraph(String(\"This is introduction\"));\n    Table([\n        TableRow([String(\"Item 1\"); String(\"$1\")]);\n        TableRow([String(\"Item 2\"); String(\"$2\")]);\n        ])\n])\n\n// пример для списка\ntype Get_function\u003c't, 'a\u003e = ('t -\u003e 'a)\ntype Update_function\u003c't, 'a\u003e = ('t -\u003e 'a -\u003e 't) \ntype Lense\u003c't, 'a\u003e =  Get_function\u003c't, 'a\u003e * Update_function\u003c't, 'a\u003e\n\n\nlet get_func lense lst = fst lense lst\nlet update_func lense lst = snd lense lst \n\nlet list_lense n = Lense(\n    (fun (lst:List\u003c't\u003e) -\u003e lst.[n]),// get\n    (fun lst new_element -\u003e List.mapi (fun i t -\u003e if i=n then new_element else t) lst) // update\n)\n\nlet lst = [0..10]\nget_func (list_lense 4) lst\n\nlet lst' = update_func (list_lense 2) lst -5\nget_func (list_lense 2) lst'\n\nlet inline (.\u003e) (xget, xset) (yget, yset) = \n   (xget \u003e\u003e yget), \n   (fun matrix element -\u003e xset matrix (yset (xget matrix) element))\n\n\nlet mtx = [[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]]\nget_func (list_lense 0 .\u003e list_lense 0) [[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]]\nupdate_func (list_lense 1 .\u003e list_lense 1) [[1; 2; 3; 4; 5]; [6; 7; 8; 9; 10]] 0\n\n\nlet body = Lense(\n    (function Body(x) -\u003e x),\n    (fun _ x -\u003e Body(x))\n)\nlet par = Lense(\n    (function Paragraph(x) -\u003e x),\n    (fun _ x -\u003e Paragraph(x))\n)\nlet table = Lense(\n    (function Table(x) -\u003e x),\n    (fun _ x -\u003e Table(x))\n)\nlet tablerow = Lense(\n    (function TableRow(x) -\u003e x),\n    (fun _ x -\u003e TableRow(x))\n)\n\nget_func (body .\u003e list_lense 2 .\u003e table .\u003e list_lense 1 .\u003e tablerow .\u003e list_lense 1) doc\n```\n\n![Гладить](/staff/cute.gif)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyashelter%2Ffsharp-learning","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyashelter%2Ffsharp-learning","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyashelter%2Ffsharp-learning/lists"}