mirror of
https://github.com/ikoHSE/sc-lectures.git
synced 2024-09-11 13:36:29 +03:00
1185 lines
21 KiB
Markdown
1185 lines
21 KiB
Markdown
---
|
||
theme: superblack
|
||
author: Ilya Kostyuchenko
|
||
---
|
||
|
||
# Функциональное программирование
|
||
|
||
|
||
:::{.notes}
|
||
Функциональное программирование это не про использование map filter
|
||
reduce. Функциональное программирование это вообще ортогонально к ооп.
|
||
ИМХО проблема с ООП -- наследование (которое тоже ортогонально к ООП)
|
||
-- нет разграничения между абстракцией и реализацией. и вообще
|
||
обзывание это функциональным программированием -- не совсем правильно.
|
||
то, что мы сейчас будем пытаться сделать -- сделать код более
|
||
переиспользуемым. сделать наши данные максимально тупыми =>
|
||
переиспользуемыми. пытаться комбинировать слишком умные куски -- не
|
||
получится -- слишком сложно. мы хотим сделать простые куски, которые
|
||
просто комбинировать и еще чтобы компилятор проверял эти наши комбинации
|
||
на валидность.
|
||
:::
|
||
|
||
---
|
||
|
||
<section data-background="images/synthwave.jpeg">
|
||
<h1>Программировать можно без боли</h1>
|
||
</section>
|
||
|
||
---
|
||
|
||
Функциональное программирование -- метапрограммирование над эффектами.
|
||
|
||
---
|
||
|
||
## Эффекты?
|
||
|
||
```{ .java .fragment }
|
||
class Cafe {
|
||
Coffee buyCoffee(CreditCard cc, Payments p) {
|
||
Coffee cup = new Coffee();
|
||
p.charge(cc, cup.price);
|
||
return cup;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
![](images/coffee-with-effects.png){width=450}
|
||
|
||
---
|
||
|
||
```{ .java }
|
||
class Cafe {
|
||
Pair<Coffee, Charge> buyCoffee(CreditCard cc) {
|
||
Coffee cup = new Coffee();
|
||
return new Pair(cup, cup.price);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
<table>
|
||
<tr>
|
||
<td>
|
||
![](images/coffee-without-effects.png){width=450}
|
||
</td>
|
||
<td>
|
||
![](images/coffee-without-effects-list.png){width=450 .fragment}
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
|
||
---
|
||
|
||
Функциональное программирование:
|
||
|
||
> функции -- это тоже данные.
|
||
|
||
`Int -> Bool` (функция) -- ровно такие же данные как и `Int`. Их также
|
||
можно складывать в структуры, передавать как аргументы и т. п.
|
||
|
||
---
|
||
|
||
# нотация
|
||
|
||
---
|
||
|
||
<table width="100%">
|
||
<tr>
|
||
<td>
|
||
```java
|
||
int x;
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
x :: Int
|
||
```
|
||
</td>
|
||
</tr>
|
||
<tr class="fragment">
|
||
<td>
|
||
```java
|
||
int f(int x)
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
f :: Int -> Int
|
||
```
|
||
</td>
|
||
</tr>
|
||
<tr class="fragment">
|
||
<td>
|
||
```java
|
||
static <T> T f(T x)
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
f :: a -> a
|
||
```
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
---
|
||
|
||
<table width="100%">
|
||
<tr>
|
||
<td>
|
||
```java
|
||
static <T> T f(T x)
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
f :: a -> a
|
||
```
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="fragment">
|
||
```java
|
||
f(x);
|
||
```
|
||
</td>
|
||
<td class="fragment">
|
||
```haskell
|
||
f x
|
||
```
|
||
</td>
|
||
</tr>
|
||
<tr class="fragment">
|
||
<td>
|
||
```java
|
||
f(x, y);
|
||
```
|
||
</td>
|
||
<td>
|
||
```{.fragment .haskell}
|
||
f x y
|
||
```
|
||
|
||
```{.fragment .haskell}
|
||
f x y = (f x) y
|
||
```
|
||
</td>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
:::{.notes}
|
||
Left-associative
|
||
Currying
|
||
:::
|
||
|
||
---
|
||
|
||
<table width="100%">
|
||
<tr>
|
||
<td>
|
||
```java
|
||
static <T> T f(T x)
|
||
static <T> T g(T x)
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
f :: a -> a
|
||
g :: a -> a
|
||
```
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="fragment">
|
||
```java
|
||
f(g(x));
|
||
g(f(x));
|
||
```
|
||
</td>
|
||
<td class="fragment">
|
||
```haskell
|
||
f (g x)
|
||
g (f x)
|
||
```
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
---
|
||
|
||
# Композиция
|
||
|
||
---
|
||
|
||
```haskell
|
||
f :: a -> a
|
||
g :: a -> a
|
||
```
|
||
|
||
``` {.fragment .haskell}
|
||
f (g x)
|
||
```
|
||
|
||
``` {.fragment .haskell}
|
||
(f . g) x == f (g x)
|
||
```
|
||
|
||
``` {.fragment .haskell}
|
||
h = f . g
|
||
h x == f (g x)
|
||
```
|
||
|
||
---
|
||
|
||
```haskell
|
||
f :: a -> a
|
||
g :: a -> a
|
||
```
|
||
|
||
```haskell
|
||
h :: a -> a
|
||
h = f . g
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
f x = x * 2
|
||
g x = x - 3
|
||
|
||
(f . g) 5
|
||
-- 4
|
||
```
|
||
|
||
<!-- ### моноид {#моноид .fragment} -->
|
||
|
||
:::{.notes}
|
||
моноид -- у тебя есть две штуки одного типа и ты можешь получить еще
|
||
одну штуку такого же типа. Это очень простой способ рождения сложности
|
||
из простоты причем тип h выводится из определения. Нам его указывать не
|
||
надо. если у нас изначальные маленькие кусочки хорошо описаны, то
|
||
порожденные большие кусочки будут автоматически хорошо описанными.
|
||
:::
|
||
|
||
<!-- ---
|
||
|
||
## Моноид
|
||
|
||
[Бинарная асоциативная операция]{.fragment}
|
||
|
||
```{.fragment .haskell}
|
||
(.) :: (a -> a) -> (a -> a) -> (a -> a)
|
||
```
|
||
|
||
[Нейтральный элемент]{.fragment}
|
||
|
||
```{.fragment .haskell}
|
||
id :: a -> a
|
||
id x = x
|
||
``` -->
|
||
|
||
---
|
||
|
||
# Haskell
|
||
|
||
---
|
||
|
||
### Чисто функциональный
|
||
|
||
Все что функция может сделать -- посмотреть на свои аргументы это
|
||
вернуть значение.
|
||
|
||
:::{.notes}
|
||
Нельзя просто произвольно распечатать строку в консоль, нельзя просто
|
||
произвольно отправить запрос на бд, нельзя просто произвольно изменить
|
||
глобальную переменную.
|
||
:::
|
||
|
||
---
|
||
|
||
### Referential transparency
|
||
|
||
##### (Любую константу можно заменить на само значение)
|
||
|
||
```{.java .fragment}
|
||
int x = 8;
|
||
int y = x++;
|
||
System.out.println(y + " might be the same as " + y);
|
||
```
|
||
|
||
```{.java .fragment}
|
||
int x = 8;
|
||
System.out.println(x++ + " might be the same as " + x++);
|
||
```
|
||
|
||
:::notes
|
||
Сильно упрощает рефакторинг и понимание кода.
|
||
:::
|
||
|
||
---
|
||
|
||
# Immuatble
|
||
|
||
## Совсем immutable
|
||
|
||
Можно не думать о порядке выражений.
|
||
|
||
---
|
||
|
||
### Нет переменных, циклов и условных переходов
|
||
|
||
[Зато есть константы и нормальная рекурсия (А переходов вообще нет)]{.fragment}
|
||
|
||
---
|
||
|
||
### Очень ленивый
|
||
|
||
```{.haskell .fragment}
|
||
xs = [4, 1, 3, 2]
|
||
xs' = sort xs
|
||
print xs
|
||
```
|
||
|
||
:::fragment
|
||
`xs'` не нужен чтобы распечатать `xs`, поэтому сортироваться ничто
|
||
не будет. (Зачем делать то, что можно не делать)
|
||
:::
|
||
|
||
---
|
||
|
||
Если хотите быстро и просто потыкаться, тут есть интерктивная штука:
|
||
|
||
[haskell.org](https://www.haskell.org)
|
||
|
||
---
|
||
|
||
# Синтаксис
|
||
|
||
---
|
||
|
||
# Функции
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
add2 :: Int -> Int
|
||
add2 x = x + 2
|
||
````
|
||
|
||
Все функции и константы всегда обозначаются словами с маленькой буквы
|
||
без пробелов.
|
||
|
||
(Константы это просто функции с нулем аргументов.)
|
||
|
||
:::notes
|
||
Мы буквально говорим что функция на таком-то аргументе равна чему-то.
|
||
:::
|
||
|
||
---
|
||
|
||
## Pattern matching
|
||
|
||
```haskell
|
||
fixBuz :: Int -> String
|
||
fixBuz 3 = "Fiz"
|
||
fixBuz 5 = "Buz"
|
||
fixBuz 15 = "FizBuz"
|
||
fixBuz _ = "Some other number"
|
||
```
|
||
|
||
Так матчить можно произвольные структуры произвольного уровня
|
||
вложенности.
|
||
|
||
`_` -- специальное название константы, которое говорит
|
||
что вам все равно что в ней лежит.
|
||
|
||
:::notes
|
||
функции объявляются в несколькро строк. Первая -- обьявленик типа
|
||
функции, а последующие -- реализация. В общем случае тип можно не
|
||
указывать, но указывать тип у высказыванй на самом верхнем уровне (не
|
||
вложенные) считается хорошим тоном и улучшает выведение типов и ошибки
|
||
компиляции. У функции может быть несколько реализаций: какая из них
|
||
вызовется зависит от значений передаваемых аргументов. матчинг
|
||
произхводится сверху вниз. если в аргемантах написано слово с маленькой
|
||
буквы, то значение аргумента биндится на эту константу.
|
||
:::
|
||
|
||
---
|
||
|
||
## Структуры
|
||
|
||
---
|
||
|
||
## Произведение типов
|
||
|
||
##### (обычные поля структур)
|
||
|
||
:::notes
|
||
После констрктора можно укзать существующие типы, которые в нем
|
||
храняться.
|
||
:::
|
||
|
||
```haskell
|
||
data PersonType = Person String Int
|
||
```
|
||
|
||
```{.haskell .fragment}
|
||
vasya :: PersonType
|
||
vasya = Person "Vasya" 8
|
||
-- тип можно не укзывать
|
||
petya = Person "Petya" 5
|
||
```
|
||
|
||
```{.haskell .fragment}
|
||
getName :: PersonType -> String
|
||
getName (Person name _) = name
|
||
```
|
||
|
||
```{.haskell .fragment}
|
||
greetPerson :: PersonType -> String
|
||
greetPerson p = "Hello, " ++ getName p
|
||
```
|
||
|
||
```{.haskell .fragment}
|
||
greetPerson petya
|
||
-- "Hello, Petya"
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
vasya = Person "Vasya" 8
|
||
petya = Person "Petya" 5
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
matchVasya :: PersonType -> Bool
|
||
matchVasya (Person "Vasya" _) = True
|
||
matchVasya _ = False
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
matchVasya vasya
|
||
-- True
|
||
|
||
matchVasya petya
|
||
-- False
|
||
```
|
||
|
||
|
||
---
|
||
|
||
```haskell
|
||
data Foo = Bar
|
||
|
||
qux :: Foo
|
||
qux = Bar
|
||
```
|
||
|
||
`Foo` -- тип структуры. `Bar` -- конструктор структуры.
|
||
|
||
Тут у `Foo` всего одно значение `Bar`.
|
||
|
||
---
|
||
|
||
## Еще немного функций
|
||
|
||
```{ .haskell .fragment }
|
||
greetPerson :: PersonType -> String
|
||
greetPerson p = "Hello, " ++ getName p
|
||
```
|
||
|
||
```{.fragment .haskell }
|
||
greetPerson :: PersonType -> String
|
||
greetPerson p = "Hello, " ++ name
|
||
where
|
||
name = getName p
|
||
```
|
||
|
||
```{.fragment .haskell }
|
||
greetPerson :: PersonType -> String
|
||
greetPerson p = "Hello, " ++ name
|
||
where
|
||
getName' (Person name _) = name
|
||
name = getName' p
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
data PersonType = Person String Int
|
||
|
||
getName :: PersonType -> String
|
||
getName (Person name _) = name
|
||
```
|
||
|
||
```{.fragment .haskell }
|
||
greetName :: String -> String
|
||
greetName name = "Hello, " ++ name
|
||
```
|
||
|
||
```{.fragment .haskell }
|
||
greetPerson :: PersonType -> String
|
||
greetPerson p = greetName (getName p)
|
||
```
|
||
|
||
```{.fragment .haskell }
|
||
greetPerson :: PersonType -> String
|
||
greetPerson = greetName . getName
|
||
```
|
||
|
||
```{.fragment .haskell }
|
||
greetPerson petya
|
||
-- "Hello, Petya"
|
||
```
|
||
|
||
```{.fragment .haskell }
|
||
(greetName . getName) petya
|
||
-- "Hello, Petya"
|
||
```
|
||
|
||
---
|
||
|
||
## Суммы типов
|
||
|
||
:::notes
|
||
Функции нескольких аргументов -- странный синтаксис -- потом расскажу. А пока используем тюпли!
|
||
|
||
В большинстве других языков этого нет и используются всякие странные кодирования типо нулевые указатели, и никто не просит тебя их проверять, что очень плохо.
|
||
:::
|
||
|
||
```{ .fragment .haskell }
|
||
data Bool = False | True
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
x :: Bool
|
||
x = True
|
||
y = False
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
ifThenElse :: (Bool, a, a) -> a
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
ifThenElse (True, a, _) = a
|
||
ifThenElse (False, _, b) = b
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
ifThenElse (True, "Hello", "World")
|
||
-- "Hello"
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
ifThenElse (False, "Hello", "World")
|
||
-- "World"
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
data CircleType = Circle Double Double Double
|
||
data RectangleType = Rectangle Double Double Double Double
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
data Shape =
|
||
CircleShape CircleType | RectangleShape RectangleType
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
surface :: Shape -> Double
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
surface (CircleShape (Circle _ _ r)) =
|
||
pi * r ^ 2
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
surface (RectangleShape (Rectangle x1 y1 x2 y2)) =
|
||
(abs (x2 - x1)) * (abs (y2 - y1))
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
shape = CircleShape (Circle 0 0 2)
|
||
surface shape
|
||
-- 12.566370614359172
|
||
```
|
||
|
||
```{ .fragment .haskell }
|
||
otherShape = RectangleShape (Rectangle 1 2 3 4)
|
||
surface otherShape
|
||
-- 4.0
|
||
```
|
||
|
||
---
|
||
|
||
# И еще немного функций
|
||
|
||
---
|
||
|
||
## Лямбда-выражения
|
||
|
||
```{ .haskell .fragment }
|
||
add8 :: Int -> Int
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add8 x = x + 8
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add8 = \x -> x + 8
|
||
```
|
||
|
||
[λ -- `\` (λ печатать тяжело)]{.fragment}
|
||
|
||
```{ .haskell .fragment }
|
||
foo :: (Int -> Int) -> Int
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
foo add8
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
foo (\x -> x + 8)
|
||
```
|
||
|
||
---
|
||
|
||
### Давайте придумаем синтаксис для функции нескольких аргументов!
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
x, y :: Int
|
||
x = 42
|
||
y = 69
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
xPlusY :: Int
|
||
xPlusY = add x y
|
||
```
|
||
|
||
[Применение функции -- лево-ассоциативно]{.fragment}
|
||
|
||
```{ .haskell .fragment }
|
||
xPlusY = (add x) y
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
xPlusY = f y
|
||
f = add x
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
f :: Int -> Int
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add :: Int -> (Int -> Int)
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
add :: Int -> (Int -> Int)
|
||
```
|
||
|
||
[Тип `->` -- право-ассоциативный]{.fragment}
|
||
|
||
```{ .haskell .fragment }
|
||
add :: Int -> Int -> Int
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add a b = a + b
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add = \a b -> a + b
|
||
```
|
||
|
||
---
|
||
|
||
Любая функция берет строго один аргумент.
|
||
|
||
Функция нескольких аргументов все равно берет строго одтн аргумент и
|
||
возвращает функцию, которая берет следующий.
|
||
|
||
*(И из-за того, что применение функции лево-ассоциативно, вызов таких не
|
||
трубует особого синтаксиса.)*
|
||
|
||
---
|
||
|
||
### Currying
|
||
|
||
```{ .haskell .fragment }
|
||
add :: Int -> Int -> Int
|
||
add a b = a + b
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add8 :: Int -> Int
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add :: Int -> (Int -> Int)
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add8 = add 8
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add8 3
|
||
-- 11
|
||
```
|
||
|
||
---
|
||
|
||
### Funny fact
|
||
|
||
[Оператор (например `+`) -- функция, название которой не содержит буквы и цифры.]{.fragment}
|
||
|
||
```{ .haskell .fragment }
|
||
x +&+ y = x + y
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
8 +&+ 9
|
||
-- 17
|
||
```
|
||
|
||
---
|
||
|
||
### Funny fact 2
|
||
|
||
[Оператор можно превратить в функцию, окружив его скобками.]{.fragment}
|
||
|
||
```{ .haskell .fragment }
|
||
8 +&+ 9
|
||
-- 17
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
(+&+) 8 9
|
||
-- 17
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add :: Int -> Int -> Int
|
||
add x y = x + y
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add = (+&+)
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add = (+)
|
||
```
|
||
|
||
---
|
||
|
||
### Funny fact 3
|
||
|
||
[Функцию можно превратить в оператор, окружив ее обратными кавычками.]{.fragment}
|
||
|
||
|
||
```{ .haskell .fragment }
|
||
add :: Int -> Int -> Int
|
||
add x y = x + y
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
add 8 9
|
||
-- 17
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
8 `add` 9
|
||
-- 17
|
||
```
|
||
|
||
---
|
||
|
||
# Список
|
||
|
||
---
|
||
|
||
### Односвязный список
|
||
|
||
```{ .haskell .fragment }
|
||
data IntList = Nil | Cons Int IntList
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
Cons :: Int -> IntList -> IntList
|
||
Nil :: IntList
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
nums :: IntList
|
||
nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
sum :: IntList -> Int
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
sum (Cons x xs) = x + sum xs
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
sum Nil = 0
|
||
sum (Cons x xs) = x + sum xs
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
sum nums
|
||
-- 6
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
take :: Int -> IntList -> IntList
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
take _ Nil = Nil
|
||
take 0 _ = Nil
|
||
take n (Cons x xs) = Cons x (take (n - 1) xs)
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
nums :: IntList
|
||
nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
take 2 nums
|
||
-- Cons 1 (Cons 2 Nil)
|
||
take 1029 nums
|
||
-- Cons 1 (Cons 2 (Cons 3 Nil))
|
||
take 0 nums
|
||
-- Nil
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
repeat :: Int -> IntList
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
repeat n = Cons n (repeat n)
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
repeat 8
|
||
-- Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (...
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
(take 3 . repeat) 8
|
||
-- Cons 8 (Cons 8 (Cons 8 Nil))
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
(sum . take 3 . repeat) 8
|
||
-- 24
|
||
```
|
||
|
||
---
|
||
|
||
<table width="100%">
|
||
<tr>
|
||
<td>
|
||
Наша самодеятельность
|
||
</td>
|
||
<td>
|
||
В стандартной библиотеке
|
||
</td>
|
||
</tr>
|
||
<tr class="fragment">
|
||
<td>
|
||
```haskell
|
||
IntList
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
[Int]
|
||
```
|
||
</td>
|
||
<tr class="fragment">
|
||
<td>
|
||
```haskell
|
||
Nil
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
[]
|
||
```
|
||
</td>
|
||
</tr>
|
||
<tr class="fragment">
|
||
<td>
|
||
```haskell
|
||
Cons
|
||
```
|
||
</td>
|
||
<td>
|
||
```haskell
|
||
:
|
||
```
|
||
</td>
|
||
</tr>
|
||
<tr class="fragment">
|
||
<td>
|
||
```haskell
|
||
Cons 3 (Cons 4 Nil)
|
||
```
|
||
</td>
|
||
<td>
|
||
``` {.fragment .haskell }
|
||
3 : 4 : []
|
||
```
|
||
|
||
``` {.fragment .haskell }
|
||
[3, 4]
|
||
```
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
data IntList = Nil | Cons Int IntList
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
data [Int] = [] | Int : [Int]
|
||
```
|
||
|
||
---
|
||
|
||
`repeat`, `sum` и `take` тоже есть в стандартной библиотеке.
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
repeat 8
|
||
-- [8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, ...
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
[8 ..]
|
||
-- [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
[8 .. 11]
|
||
-- [8, 9, 10, 11]
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
[8 .. 8]
|
||
-- [8]
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
[8 .. 5]
|
||
-- []
|
||
```
|
||
|
||
---
|
||
|
||
# QuickSort
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
quicksort :: [Int] -> [Int]
|
||
```
|
||
|
||
```{ .haskell .fragment}
|
||
quicksort (x:xs) =
|
||
quicksort smaller ++ [x] ++ quicksort larger
|
||
where
|
||
smaller = filter (< x) xs
|
||
larger = filter (>= x) xs
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
filter :: (Bool -> Int) -> [Int] -> [Int]
|
||
```
|
||
|
||
```{ .haskell .fragment}
|
||
filter f (x:xs) =
|
||
if f x
|
||
then x:(filter f xs)
|
||
else filter f xs
|
||
```
|
||
|
||
```{ .haskell .fragment}
|
||
filter _ [] = []
|
||
filter f (x:xs) =
|
||
if f x
|
||
then filter f xs
|
||
else x:(filter f xs)
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
quicksort :: [Int] -> [Int]
|
||
quicksort (x:xs) =
|
||
quicksort smaller ++ [x] ++ quicksort larger
|
||
where
|
||
smaller = filter (< x) xs
|
||
larger = filter (>= x) xs
|
||
filter _ [] = []
|
||
filter f (x:xs) =
|
||
if f x
|
||
then filter f xs
|
||
else x:(filter f xs)
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
quicksort :: [Int] -> [Int]
|
||
quicksort [] = []
|
||
quicksort (x:xs) =
|
||
quicksort smaller ++ [x] ++ quicksort larger
|
||
where
|
||
smaller = filter (< x) xs
|
||
larger = filter (>= x) xs
|
||
filter _ [] = []
|
||
filter f (x:xs) =
|
||
if f x
|
||
then filter f xs
|
||
else x:(filter f xs)
|
||
```
|
||
|
||
```{ .haskell .fragment}
|
||
quicksort [2, 1, 3, 4]
|
||
-- [1, 2, 3, 4]
|
||
```
|
||
|
||
---
|
||
|
||
`filter` тоже есть в стандартной библиотеке.
|
||
|
||
---
|
||
|
||
# Еще немного структур
|
||
|
||
---
|
||
|
||
## Параметрический полиморфизм
|
||
|
||
### (Дженерики)
|
||
|
||
```{ .haskell .fragment }
|
||
data IntList = Cons Int IntList | Nil
|
||
```
|
||
|
||
:::notes
|
||
Мы научиоись делать список интов. А что если мы хотим сделать просто список. Чего-нибудь.
|
||
:::
|
||
|
||
```{ .haskell .fragment }
|
||
data List a = Cons a (List a) | Nil
|
||
```
|
||
|
||
```{ .java .fragment }
|
||
class List<T> { ...
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
ints :: List Int
|
||
ints = Cons 1 (Cons 2 (Cons 3 Nil))
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
-- Типы как всегда можно не писать
|
||
strings :: List String
|
||
strings = Cons "one" (Cons "two" (Cons "three" Nil))
|
||
```
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
things = Cons "one" (Cons 2 Nil)
|
||
```
|
||
|
||
```fragment
|
||
• Couldn't match type ‘Int’ with ‘String’
|
||
Expected type: List String
|
||
Actual type: List Int
|
||
|
|
||
10 | things = Cons "one" (Cons 2 Nil)
|
||
| ^^^^^^^^^^^^
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
data List a = Cons a (List a) | Nil
|
||
```
|
||
|
||
[`a` должен всегда быть `a`.]{.fragment}
|
||
|
||
---
|
||
|
||
```{ .haskell }
|
||
take :: Int -> IntList -> IntList
|
||
take _ Nil = Nil
|
||
take 0 _ = Nil
|
||
take n (Cons x xs) = Cons x (take (n - 1) xs)
|
||
```
|
||
|
||
```{ .haskell .fragment }
|
||
take :: Int -> List a -> List a
|
||
take _ Nil = Nil
|
||
take 0 _ = Nil
|
||
take n (Cons x xs) = Cons x (take (n - 1) xs)
|
||
```
|
||
|
||
:::notes
|
||
Поменялся только тип!
|
||
:::
|
||
|
||
---
|
||
|
||
# Где и как смотреть "стандартную библиотеку"
|
||
|
||
---
|
||
|
||
1. [Hackage](https://hackage.haskell.org/package/base)
|
||
|
||
(Там вам нужен только пакет `base`. Ссылка ведет прямо на него.)
|
||
Еще если там нажать `s`, то будет поиск.
|
||
|
||
2. [Hoogle](https://hoogle.haskell.org)
|
||
|
||
Это поиск по типам. Например: `Int -> [Int] -> [Int]` (Тут
|
||
вам опять же нужен только пакет `base`. Нужно чтобы справа было
|
||
\"package:base\".)
|