Числовых типов много — это типы, которые реализуют класс Num:
2 :+ 3
, имеется в виду $2 + 3i$import Data.Complex
(2 :+ 3) * (1 :+ (-1)) -- (2+3i)(1-i) = 5+i
5.0 :+ 1.0
Попробуем изучить, как устроен тип Ratio
, он означает дроби типа 2/3 или 5/7, с ними можно производить арифметические операции. Значение этого типа создаётся с помощью конструктора :%
(не доступен для нашего использования). Нам доступна функция (%), которая требует, чтобы оба аргумента были целыми числами (класс Integral
).
import Data.Ratio
2 % 3
1 % 3 + 1 % 6 -- 1/3 + 1/6 = 1/2
2 % 3
1 % 2
Тип type Rational = Ratio Integer
, т.е. отношение целых чисел реализует классы Eq
, Ord
, т.е. значения можно сравнивать:
1 % 2 < 2 % 3
True
Еще Rational
реализует класс Num
, посмотрим, наконец, что можно делать с Num
: +
, -
, *
, abs
, signum
, negate
, fromInteger
:
negate $ 2 % 3
signum $ (-2) % (-6) -- получаем 1 % 1, знак должен тоже иметь
(fromInteger 42)::Rational -- можно рассмотреть результат как рациональное число
-- (42::Integer) + (1 % 2) -- Integer и Rational не сложить вместе
fromInteger (42::Integer) + (1 % 2) -- надо преобразовать с помощью fromInteger
(-2) % 3
1 % 1
42 % 1
85 % 2
Кроме того Rational
реализует классы Real
и RealFrac
. Посмотрим, что должны уметь значения типов, принадлежащих классу Real
. Оказывается, для класса Real
надо иметь класс Num
(быть числом), Ord
(быть упорядоченым) и реализовывать метод toRational :: a -> Rational
.
toRational $ 2 % 3 -- для рациональных чисел превращение в рациональное ничего не делает
2 % 3
Кто еще реализует Real
: Double
, Float
, Int
, Integer
:
toRational 42
toRational 0.625 -- хранится без потери точности (в двоичной системе счисления = 0.конечное число)
toRational 4.2
toRational pi
42 % 1
5 % 8
4728779608739021 % 1125899906842624
884279719003555 % 281474976710656
А что означает класс RealFrac
: нужно реализовывать классы Real
(уже изучили), Fractional
(изучим следующим) и функции: properFraction
, truncate
, round
, ceiling
, floor
(округление к 0, округление, округление вверх, округление вниз):
properFraction $ 42 % 9 -- 4 + 2/3
round $ 42 % 9 -- округление
:type properFraction
(4,2 % 3)
5
Типы Double
и Float
тоже реализуют RealFrac
, поэтому их тоже можно округлять:
round 4.2
properFraction (4.5)
properFraction (4.2)
4
(4,0.5)
(4,0.20000000000000018)
Еще для Ratio реализуется класс Fractional
: фактически, это числа, которые можно делить друг на друга. Есть методы fromRational
, recip
, (\)
. Типы Double
, Float
тоже имеют этот класс
-- обратное число
recip $ 2 % 3
recip 0.5
(2 % 3) / (5 % 6)
0.5 / 2.5
-- (0.5::Double) / (2 % 3) -- Double на Rational не поделить
(toRational (0.5::Double)) / (2 % 3) -- приходится превращать в дробь или
(0.5::Double) / fromRational (2 % 3) -- превращать дробь в вещественное
3 % 2
2.0
4 % 5
0.2
3 % 4
0.75
Отдельные функции для Rational
:
numerator (2 % 3)
denominator (2 % 3)
approxRational pi 0.001 -- округли pi с точностью до 0.0001
approxRational pi 0.02
2
3
201 % 64
22 % 7
Нужен инструмент, который позовляет собирать вашу программу, разбитую на несколько файлов с исходными кодами и использует сторонние библиотеки. (build tool). Самые распространенные для Haskell — это Cabal и Stack. Cabal более ранний, Stack частично его использует и решает часть его проблем. Stack основан на принципе, что разные сборки одной и той же программы должны получаться одинаковыми. В Cabal это не так, сегодня сборка прошла одним образом, завтра одна из сторонних библиотек обновилсась, ваша программа собралась по-другому. В хорошем случае в библиотеке была исправлена какая-то ошибка, и ваша программа стала работать лучше. В плохом случае, ваша программа перестанет собираться из-за изменения какой-нибудь функции в билиотеке.
Чтобы Stack мог собирать программу одинаково, он пользуется особым источником сторонних библиотек, в котором может не быть последних версий этих библиотек.
Хорошая новость, между проектами на Stack и на Cabal легко переходить, т.е. можно начать на Stack, потом использовать Cabal и наоборот.
У нас будем использовать Stack: Установка и использование. Установка и использование. Более подробное руководство по использованию.
Пользуемся из командной строки (или установите plugin к IDEA).
Команда stack new my-project
создаёт новый проект: stack new projName [необязательный шаблон]
. Получаем каталог my-project
со следующими файлами (часть этих файлов появится чуть позже)
my-project/
├── app
│ └── Main.hs
├── ChangeLog.md
├── LICENSE
├── .gitignore
├── my-project.cabal
├── package.yaml
├── README.md
├── Setup.hs
├── .stack-work - ...
├── src
│ └── Lib.hs
├── stack.yaml
├── stack.yaml.lock
└── test
└── Spec.hs
Перед тем, как смотреть, что есть в каталоге, давайте попробуем запустить проект. Вводим команду stack run
, выводится результат someFunc
(вместо Hello World
)
Что внутри каталога:
app
содержит код, который запускает программу, там есть функция Mainsrc
содержит все исходники, именно там надо писать код, именно в Lib.hs реализована функция someFunc, которая вызывается из Main при старте программы.test
содержит код для тестирования программы.ChangeLog.md
, LICENSE
, README.md
, .gitignore
— стандартные файлы для программного проекта, выложенного в .git репозиторий.my-project.cabal
— это настройка вашего проекта для инструмента Cabal, файл можно читать, там понятно написано, какие есть библиотеки, где исходный код, кто автор и т.п. Из-за того, что есть этот файл, Stack собирает программу с его помощью, вы можете начать использовать Cabal вместо Stack в любой момент. Но (!!!) этот файл нельзя редактировать, пока вы пользуетесь Stack, потому что Stack создает его на основе других настроек.stack.yaml
это настройки проекта для Stack.stack.yaml.lock
содержит информацию об используемых версиях библиотек, чтобы при новой сборке программе использовать те же самые..stack-work
— каталог, куда загружается компилятор Haskell нужной вам версии, и все библоиотеки. Благодаря тому, что в каждом проекте свой .stack-work
, все проекты могут пользоваться разными версиями комплятора и библиотек.