Функциональное программирование
Функциональное программирование:
функции – это тоже данные.
Int -> Bool
(функция) – ровно такие же данные как и Int
. Их также можно складывать в структуры, передавать как аргументы и т. п.
static <T> T f(T x)
static <T> T g(T x)
|
|
|
|
моноид
Моноид
Бинарная асоциативная операция
(.) :: (a -> a) -> (a -> a) -> (a -> a)
Нейтральный элемент
Чисто функционаьлный
Все что функция может сделать – посмотреть на свои аргументы это вернуть значение.
Referential transparency
(Любую константу можно заменить на само значение)
int x = 8;
int y = x++;
System.out.println(y + " might be the same as " + y);
int x = 8;
System.out.println(x++ + " might be the same as " + x++);
Нет переменных, циклов и условных переходов
Зато есть константы и нормальная рекурсия (А переходов вообще нет)
Очень ленивый
xs = [4, 1, 3, 2]
xs' = sort xs
print xs
xs'
не нужен чтобы распечатать xs
, поэтому сортироваться ничто не будет. (Зачем делать то, что можно не делать)
Если хотите быстро и просто потыкаться, тут есть интерктивная штука:
haskell.org
add2 :: Int -> Int
add2 x = x + 2
Все функции и константы всегда обозначаются словами с маленькой буквы без пробелов.
(Константы это просто функции с нулем аргументов.)
Pattern mathcing
fixBuz :: Int -> String
divide8 3 = "Fiz"
divide8 5 = "Buz"
divide8 15 = "FizBuz"
divide8 _ = "Some other number"
Так матчить можно произвольные структуры произвольного уровня вложенности.
_
– специальное название константы, которое говорит что вам все равно что в ней лежит.
data Foo = Bar
foo :: Bar
foo = Bar
Foo
– тип структуры. Bar
– конструктор структуры.
Тут у Foo
всего одно значение Bar
.
Произведение типов
(обычные поля структур)
data PersonType = Person String Int
vasya :: PersonType
vasya = Person "Vasya" 8
-- тип можно не укзывать
petya = Person "Petya" 5
getName :: PersonType -> String
getName (Person name _) = name
greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ getName p
greetPerson petya
-- "Hello, Petya"
Еще немного функций
greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ getName p
greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ name
where
name = getName p
greetPerson :: PersonType -> String
greetPerson p = "Hello, " ++ name
where
getName' (Person name _) = name
name = getName' p
data PersonType = Person String Int
getName :: PersonType -> String
getName (Person name _) = name
greetName :: String -> String
greetName name = "Hello, " ++ name
greetPerson :: PersonType -> String
greetPerson p = greetName (getName p)
greetPerson :: PersonType -> String
greetPerson = greetName . getName
greetPerson petya
-- "Hello, Petya"
(greetName . getName) petya
-- "Hello, Petya"
Суммы типов
x :: Bool
x = True
y = False
ifThenElse :: (Bool, a, a) -> a
ifThenElse (True, a, _) = a
ifThenElse (False, _, b) = b
ifThenElse (True, "Hello", "World")
-- "Hello"
ifThenElse (False, "Hello", "World")
-- "World"
data CircleType = Circle Double Double Double
data RectangleType = Rectangle Double Double Double Double
data Shape =
CircleShape CircleType | RectangleShape RectangleType
surface :: Shape -> Double
surface (CircleShape (Circle _ _ r)) =
pi * r ^ 2
surface (RectangleShape (Rectangle x1 y1 x2 y2)) =
(abs (x2 - x1)) * (abs (y2 - y1))
shape = CircleShape (Circle 0 0 2)
surface shape
-- 12.566370614359172
otherShape = RectangleShape (Rectangle 1 2 3 4)
surface otherShape
-- 4.0
Лямбда-выражения
λ – \
(λ печатать тяжело)
foo :: (Int -> Int) -> Int
Давайте придумаем синтаксис для функции нескольких аргументов!
x, y :: Int
x = 42
y = 69
xPlusY :: Int
xPlusY = add x y
Применение функции – лево-ассоциативно
add :: Int -> (Int -> Int)
add :: Int -> (Int -> Int)
Тип ->
– право-ассоциативный
Любая функция берет строго один аргумент.
Функция нескольких аргументов все равно берет строго одтн аргумент и возвращает функцию, которая берет следйющий.
(И из-за того, что применение функции лево-ассоциативно, вызов таких не трубует особого синтаксиса.)
Currying
add :: Int -> Int -> Int
add a b = a + b
add :: Int -> (Int -> Int)
Funny fact
Оператор (например +
) – функция, название которой не содержит буквы и цифры.
Funny fact 2
Оператор можно превратить в функцию, окружив его скобками.
add :: Int -> Int -> Int
add x y = x + y
Funny fact 3
Функцию можно превратить в оператор, окружив ее обратными кавычками.
add :: Int -> Int -> Int
add x y = x + y
Односвязный список
data IntList = Cons Int IntList | Nil
Cons :: Int -> IntList -> IntList
Nil :: IntList
nums :: IntList
nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
sum (Cons x xs) = x + sum xs
sum Nil = 0
sum (Cons x xs) = x + sum xs
take :: Int -> IntList -> IntList
take _ Nil = Nil
take 0 _ = Nil
take n (Cons x xs) = Cons x (take (n - 1) xs)
nums :: IntList
nums = 1 `Cons` (2 `Cons` (3 `Cons` Nil))
take 2 nums
-- Cons 1 (Cons 2 Nil)
take 1029 nums
-- Cons 1 (Cons 2 (Cons 3 Nil))
take 0 nums
-- Nil
repeat n = Cons n (repeat n)
repeat 8
-- Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (Cons 8 (...
(take 3 . repeat) 8
-- Cons 8 (Cons 8 (Cons 8 Nil))
(sum . take 3 . repeat) 8
-- 24
Наша самодеятельность
|
В стандартной библиотеке
|
|
|
|
|
|
|
|
|
|
|
repeat
, sum
и take
тоже есть в стандартной библиотеке.
quicksort :: [Int] -> [Int]
quicksort (x:xs) =
quicksort smaller ++ [x] ++ quicksort larger
where
smaller = filter (< x) xs
larger = filter (>= x) xs
filter :: (Bool -> Int) -> [Int] -> [Int]
filter f (x:xs) =
if f x
then filter f xs
else x:(filter f xs)
filter _ [] = []
filter f (x:xs) =
if f x
then filter f xs
else x:(filter f xs)
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)
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)
quicksort [2, 1, 3, 4]
-- [1, 2, 3, 4]
filter
тоже есть в стандартной библиотеке.
Где и как смотреть “стандартную библиотеку”
Hackage
(Там вам нужен только пакет base
. Ссылка ведет прямо на него.) Еще если там нажать s
, то будет поиск.
Hoogle
Это поиск по типам. Например: Int -> [Int] -> [Int]
(Тут вам опять же нужен только пакет base
. Нужно чтобы справа было "package:base".)