{ "cells": [ { "cell_type": "markdown", "id": "d5547c7a", "metadata": {}, "source": [ "# Еще несколько функций высшего порядка\n", "\n", "Давайте введем функцию, которая меняет местами аргументы другой функции:" ] }, { "cell_type": "code", "execution_count": 6, "id": "2bcd03e3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Redundant lambda
Found:
flip' f = \\ x y -> f y x
Why Not:
flip' f x y = f y x
Avoid lambda
Found:
\\ x y -> f y x
Why Not:
flip f
" ], "text/plain": [ "Line 3: Redundant lambda\n", "Found:\n", "flip' f = \\ x y -> f y x\n", "Why not:\n", "flip' f x y = f y xLine 3: Avoid lambda\n", "Found:\n", "\\ x y -> f y x\n", "Why not:\n", "flip f" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "1" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "5.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "--filp' :: (a -> b -> c) -> (b -> a -> c)\n", "flip' :: (a -> b -> c) -> b -> a -> c\n", "flip' f = \\x y -> f y x\n", "\n", "revminus = flip' (-)\n", "revminus 2 3\n", "\n", "flip' (-) 2 3\n", "\n", "flip'' :: (a -> b -> c) -> b -> a -> c\n", "flip'' f x y = f y x\n", "\n", "flip'' (/) 2 10" ] }, { "cell_type": "code", "execution_count": 9, "id": "bf609645", "metadata": {}, "outputs": [], "source": [ "--f = \\(h:t) -> h + 1\n", "--f [10, 20, 30]" ] }, { "cell_type": "markdown", "id": "1487f3e3", "metadata": {}, "source": [ "Вывод: пользуйтесь `flip`.\n", "\n", "Функция применения `$`:" ] }, { "cell_type": "code", "execution_count": 18, "id": "ba44b060", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Redundant $
Found:
addOne $ 10
Why Not:
addOne 10
" ], "text/plain": [ "Line 10: Redundant $\n", "Found:\n", "addOne $ 10\n", "Why not:\n", "addOne 10" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "11" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "11" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "11" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "5.5" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "6.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "($*) :: (a -> b) -> a -> b\n", "f $* x = f x\n", "\n", "addOne = (+1)\n", "\n", "addOne 10\n", "\n", "addOne $* 10\n", "\n", "addOne $ 10\n", "\n", "-- пример, где есть разница\n", "\n", "addOne 10 / 2\n", "\n", "addOne $ 10 / 2\n", "\n", "addOne (10 / 2)" ] }, { "cell_type": "markdown", "id": "6208e06b", "metadata": {}, "source": [ "Получается, что `$` иногда удобна, чтобы писать меньше скобок. Но будьте с ней аккуратны, у нее правая ассоциативность:" ] }, { "cell_type": "code", "execution_count": 23, "id": "6d0861d0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Redundant $
Found:
(+) $ (2 * 2)
Why Not:
(+) (2 * 2)
Redundant $
Found:
((+) $ (2 * 2)) $ (3 * 3)
Why Not:
((+) $ (2 * 2)) (3 * 3)
" ], "text/plain": [ "Line 3: Redundant $\n", "Found:\n", "(+) $ (2 * 2)\n", "Why not:\n", "(+) (2 * 2)Line 3: Redundant $\n", "Found:\n", "((+) $ (2 * 2)) $ (3 * 3)\n", "Why not:\n", "((+) $ (2 * 2)) (3 * 3)" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "13" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "13" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "20" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(+) (2*2) (3*3)\n", "-- (+) $ (2*2) $ (3*3) -- пытается сначала посчитать (2*2) $ (3*3)\n", "((+) $ (2*2)) $ (3*3) -- слишком много скобок\n", "(*2) $ (+1) $ 3*3 -- сначала +1, потом *2" ] }, { "cell_type": "markdown", "id": "2b30ecaa", "metadata": {}, "source": [ "Еще одна функция высшего порядка: `.`. Композиция функций, как в математике: $h = g \\circ f$, тогда по определению, $h(x) = g(f(x))$. В Haskell есть аналогичная функция `.`:" ] }, { "cell_type": "code", "execution_count": 25, "id": "9411c906", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "85" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "(.*) :: (b -> c) -> (a -> b) -> (a -> c)\n", "-- g .* f = \\x -> g (f x)\n", "(g .* f) x = g $ f x\n", "\n", "fun = (+1) . (*2)\n", "fun 42" ] }, { "cell_type": "code", "execution_count": 30, "id": "2fc30fac", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Use even
Found:
x `mod` 2 == 0
Why Not:
even x
" ], "text/plain": [ "Line 1: Use even\n", "Found:\n", "x `mod` 2 == 0\n", "Why not:\n", "even x" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "[2,4,6,8,10]" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "[2,4,6,8,10]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "filter (\\x -> x `mod` 2 == 0) [1..10]\n", "filter ((==0) . (`mod` 2)) [1..10]" ] }, { "cell_type": "markdown", "id": "6e3196e6", "metadata": {}, "source": [ "# Алгебраические типы данных\n", "\n", "Введем для примера понятие человека, который может быть студентом или преподавателем" ] }, { "cell_type": "code", "execution_count": 37, "id": "de7f4460", "metadata": {}, "outputs": [], "source": [ "-- студент имя, курс, преподаватель только имя\n", "-- deriving Show, заклинание, которое позволяет распечатывать значения\n", "data Human = Student String Int | Lecturer String deriving Show" ] }, { "cell_type": "markdown", "id": "b540f53f", "metadata": {}, "source": [ "Это описание типа. Т.е. есть тип Человек, значения этого типа можно создать с помощью двух \"конструкторов типа\" `Student` и `Lecturer`. При вызове первого конструктора нужно указать String и Int. При вызове второго — только String:" ] }, { "cell_type": "code", "execution_count": 39, "id": "21da4377", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "Student \"John\" 1" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "human1 = Student \"John\" 1\n", "human2 = Student \"Mary\" 5\n", "human3 = Lecturer \"Ilya\"\n", "\n", "human1 -- работает show human1" ] }, { "cell_type": "markdown", "id": "450ec7a0", "metadata": {}, "source": [ "Получается, что конструкторы типов работают как функции:" ] }, { "cell_type": "code", "execution_count": 40, "id": "239fa178", "metadata": {}, "outputs": [ { "data": { "text/html": [ "Student :: String -> Int -> Human" ], "text/plain": [ "Student :: String -> Int -> Human" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "Lecturer :: String -> Human" ], "text/plain": [ "Lecturer :: String -> Human" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ":type Student\n", ":type Lecturer" ] }, { "cell_type": "markdown", "id": "9176b876", "metadata": {}, "source": [ "И их действительно можно использовать как функции:" ] }, { "cell_type": "code", "execution_count": 41, "id": "0683ebf0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Lecturer \"Ilya\",Lecturer \"Pavel Petrovich\"]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "map Lecturer [\"Ilya\", \"Pavel Petrovich\"]" ] }, { "cell_type": "markdown", "id": "46cb0ac7", "metadata": {}, "source": [ "Пример, в котором конструктор типа называется так же как и тип. Это часто случается, надо уметь не путать где тип, а где конструктор:" ] }, { "cell_type": "code", "execution_count": 45, "id": "997e4c40", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Point 1 2" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "data Point = Point Int Int deriving Show\n", "\n", "Point 1 2 -- использую конструктор типа\n", "\n", "f :: Point -> Int -- здесь это тип\n", "-- чтобы реализовать функцию, используется сопоставление с образцом\n", "-- через конструкторы типа\n", "f (Point x y) = abs x + abs y -- норма 1ой степени, здесь конструктор\n", "\n", "f (Point 2 3)" ] }, { "cell_type": "markdown", "id": "3397b41f", "metadata": {}, "source": [ "Пример функции для `Human`:" ] }, { "cell_type": "code", "execution_count": 47, "id": "00cb1e13", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"John\"" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\"Mary\"" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "\"Ilya\"" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "getName :: Human -> String\n", "getName (Student name _) = name -- номер курса не интересен, _\n", "getName (Lecturer name) = name\n", "\n", "getName human1\n", "getName human2\n", "getName human3" ] }, { "cell_type": "markdown", "id": "dee7013d", "metadata": {}, "source": [ "Кстати, типы и конструкторы типов в Haskell должны начинаться с заглавной буквы.\n", "\n", "Алгебраические типы могут быть параметризованы другим типом. Введём тип \"Пара\":" ] }, { "cell_type": "code", "execution_count": 59, "id": "fd0c03f0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Pair 3 2" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "Pair \"XYZ\" \"ABC\"" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "Pair 6 7" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "data Pair a = Pair a a deriving Show\n", "\n", "p1 = Pair 2 3\n", "p2 = Pair \"ABC\" \"XYZ\"\n", "-- p3 = Pair 2 \"ABC\" -- не одинаковый тип\n", "\n", "swap :: Pair a -> Pair a\n", "swap (Pair x y) = Pair y x\n", "\n", "swap p1\n", "swap $ Pair \"ABC\" \"XYZ\"\n", "\n", "inc :: Pair Int -> Pair Int\n", "inc (Pair x y) = Pair (x + 1) (y + 1)\n", "\n", "inc $ Pair 5 6" ] }, { "cell_type": "markdown", "id": "808d34d8", "metadata": {}, "source": [ "Т.е. `Pair` сам по себе как тип использовать нельзя, после него обязательно должен быть написан еще тип. Произвольный `a` или конкретный, например, `Int`.\n", "\n", "## Рекурсивный алгебраический тип\n", "Алгебраический тим можно определять через себя. Давайте заведем свой список:" ] }, { "cell_type": "code", "execution_count": 88, "id": "c30e7373", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "-- список чисел\n", "data List = Empty | Node Int List deriving Show\n", "\n", "lst1 = Empty\n", "lst2 = Node 10 Empty\n", "lst3 = Node 20 lst2\n", "lst33 = Node 20 (Node 10 Empty) -- аналогично предыдущему\n", "lst4 = Node 10 (Node 20 (Node 30 Empty))\n", "\n", "data List' = Nil | (:*) Int List' deriving Show\n", "\n", "lst1' = Nil\n", "lst2' = 10 :* Nil\n", "lst3' = 10 :* (20 :* Nil)\n", "\n", "length' :: List' -> Int\n", "length' Nil = 0\n", "length' (h :* t) = 1 + length' t\n", "\n", "length' lst3'" ] }, { "cell_type": "markdown", "id": "cd94f4f5", "metadata": {}, "source": [ "А теперь объединим рекурсивные типы и параметризованные типы, сделаем список, в котором можно хранить значения произвольного типа." ] }, { "cell_type": "code", "execution_count": 91, "id": "f8d5c484", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(:*) \"abc\" ((:*) \"xyz\" Nil)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "data List a = Nil | (:*) a (List a) deriving Show\n", "\n", "\"abc\" :* (\"xyz\" :* Nil) -- соответствует \"списку\" [\"abc\", \"xyz\"]" ] }, { "cell_type": "markdown", "id": "ca8dbaae", "metadata": {}, "source": [ "Анонс:" ] }, { "cell_type": "code", "execution_count": 92, "id": "8981c342", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 15 [1..]" ] }, { "cell_type": "code", "execution_count": 93, "id": "376e3d2d", "metadata": {}, "outputs": [], "source": [ "ones = 1 : ones" ] }, { "cell_type": "code", "execution_count": 94, "id": "b0821d42", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "take 15 ones" ] }, { "cell_type": "markdown", "id": "4876a111", "metadata": {}, "source": [ "Будет задача. `fib = ...` бесконечный список чисел Фибоначчи." ] } ], "metadata": { "kernelspec": { "display_name": "Haskell", "language": "haskell", "name": "haskell" }, "language_info": { "codemirror_mode": "ihaskell", "file_extension": ".hs", "mimetype": "text/x-haskell", "name": "haskell", "pygments_lexer": "Haskell", "version": "8.10.4" } }, "nbformat": 4, "nbformat_minor": 5 }