Привет Habr! Сегодня я достал свои старые лекции по курсу «Haskell как первый язык программирования» Сергея Михайловича Абрамова и попробую максимально доходчиво и с примерами рассказать об этом замечательном языке тем, кто с ним еще не знаком. Рассказ ориентирован на неподготовленного читателя. Так что, даже если вы впервые услышали слово Haskell…
Базовые типы Haskell
Базовые типы языка Haskell — это:
Числа
Логические величины
Символы
Списки
Упорядоченные множества (tuples)
Функции
Числа
Целые:
Integer (-∞,∞)
Int (-2^31, 2^31-1)
В прелюдии (стандартной библиотеке) определенно много полезных функций для целых чисел, в том числе, и преобразование в число с плавающей точкой (fromInt и fromInteger)
Числа с плавающей точкой:
Float (7 знаков после запятой)
Double (16 знаков после запятой)
Логические величины
Bool (True | False)
Операции конъюнкции, дизъюнкции и отрицания (&&, ||, not)
Символы
Char (’a’)
И функции Char в Int и Int в Char (ord, chr)
Списки
Списки могут быть разные:
[Int] — список целых чисел [1,2,3,4]
[Char] — список символов (строка)
[[Int]] — массив
[Float -> Float] — это список функций
и т. д.
Несколько стандартных операций в примерах:
Main> head [1,2,3]
1
Main> tail [1,2,3]
[2,3]
Main> length [True,False]
2
Main> reverse [1,2,3]
[3,2,1]
Main> 0:[1,2,3]
[0,1,2,3]
Main> — строка приглашения в консоли компилятора ghci
«:» — операция присоединения элемента к списку.
Упорядоченные множества
Примеры:
(2.4, ”cat”) (Float, [Char])
(’a’, True, 1) (Char, Bool, Int)
([1,2],sqrt) ([Int], Float->Float)
(1, (2, 3)) (Int, (Int, Int))
Но, сердце Haskell и всего функционального программирования — это, конечно, сами функции!
Функции
Функция, в современной математике, это закон соответствия, который сопоставляет каждому элементу x из данного множества A один единственный (или ни одного) элемент y из множества B.
Haskell, по своему назначению, это, прежде всего, язык математиков, поэтому синтаксис тут максимально точно соответствует этому определению.
Пример:
Square :: Integer -> Integer
Square x = x*x
Как вы, наверное, догадались, это функция возведения числа в квадрат. Разберем её подробно:
Первая строка — это объявление функции:
Имя_функции :: область_определения — > область _значений
Square :: Integer -> Integer
Тут следует сказать, что в Haskell совсем необязательно всегда определять функцию. В ряде случаев интерпретатор и так поймет какие у данной функции области определения и значения. Однако, опускать объявления — моветон.
Вторая строка — это определение функции:
Имя_функции параметры = правило_вычисления
Square x = x*x
Функция без параметров есть ничто иное, как константа:
e :: Float
e = exp 1.0
Функция с несколькими параметрами:
abcFormula :: Float -> Float -> Float -> [Float]
abcFormula a b c = [
(-b+sqrt(b*b-4.0*a*c))/(2.0*a),
(-b-sqrt(b*b-4.0*a*c))/(2.0*a)
]
-- находит корни уравнения ax^2+bx+c=0
Определения функций с альтернативами
Как и в любом языке, в Haskell есть конструкции ветвления.
Разберем их на примере функции abs (модуль).
If … then … else …
abs1 x = if x>=0 then x else -x
Case … of …
abs2 x = case x>=0 of
True -> x
False -> -x
Но, помимо стандартных if и case, в Haskell есть очень красивая и наиболее используемая конструкция ветвления. Так называемые, охранные выражения. Пример:
abs3 x | x>0 = x
| x<0 = -x
| otherwise = 0
Прямую черту следует читать, как: «при».
Читаем: «Функция abs3, с входным параметром x, при x>0 принимает значение x, при x<0 принимает значение -x, и в любом другом случае принимает значение 0».
Конечно, мы могли записать все с помощью двух охранных выражений, но я записал три, что бы было понятно, что их может быть сколько угодно.
Otherwise в прелюдии определен очень просто:
otherwise :: Bool
otherwise = True
То есть, можно спокойно написать вместо «otherwise» «True», но это, опять же, моветон.
Сопоставление с образцом
Один из наиболее распространенных и эффективных приемов в Haskell — это сопоставление с образцом. Вместо параметра мы можем подсунуть функции пример того, как должен выглядеть параметр. Если образец подошел функция выполняется, если нет — переходит к следующему образцу. Например, определение факториала через рекурсию с помощью образцов:
fact :: Integer -> Integer
fact 0 = 1
fact n = (n+1) * fact (n)
Тоже самое, но, с помощью охранных выражений:
fact :: Integer -> Integer
fact n | n==0 = 1
| n>0 = n*fact (n-1)
Есть очень распространенный образец для списка: (x:xs). X — обозначает один элемент, XS — остальной список (кроме первого элемента). «:» — операция присоединения элемента к списку. Примеры из прелюдии:
head :: [a] -> a
head (x:_) = x
head [] = error "Prelude.head: empty list"
tail :: [a] -> [a]
tail (_:xs) = xs
tail [] = error "Prelude.tail: empty list"
Функция head принимает на вход список чего угодно [a] и возвращает первый элемент этого списка. Функция tail принимает на вход список чего угодно [a] и изымает из этого списка первый элемент.
«_» — означает, что элемент находящийся тут нас не интересует.
Ну вот, на сегодня и все. Если будет интерес, в ближайшее время напишу продолжение.
Автор: serr