Целые числа
В Haskell предусмотрено два типа для целых чисел. Это Integerи Int. Чем они отличаются? Значения
типа Integerне ограничены, мы можем проводить вычисления с очень-очень-очень большими числами, если
памяти на нашем компьютере хватит. Числа из типа Intограничены. Каждое число занимает определённый
размер в памяти компьютера. Диапазон значений для Intсоставляет от − 229 до 229 − 1. Вычисления с Int
более эффективны.
Действительные числа
Действительные числа бывают дробными (тип Rational), с ординарной точностью Floatи с двойной
точностью Double. Числа из типа Floatзанимают меньше места, но они не такие точные как Double. Если вы
сомневаетесь, чем пользоваться, выбирайте Double, обычно Floatиспользуется только там, где необходимо
хранить огромные массивы чисел. В этом случае мы экономим много памяти.
Преобразование численных типов
Во многих языках программирования при сложении или умножении чисел разных типов проводится ав-
томатическое приведение типов. Обычно целые числа становятся действительными, Floatпревращается в
Doubleи так далее. Это противоречит строгой типизации, поэтому в Haskell этого нет:
Prelude>(1 ::Int) +(1 ::Double)
<interactive >:2 :13 :
Couldn’tmatch expected type‘ Int’with actual type‘ Double’
Inthe second argument of‘( +)’, namely ‘(1 :: Double)’
Inthe expression :(1 :: Int) +(1 :: Double)
Inan equation for ‘it’ :it =(1 :: Int) +(1 :: Double)
Любое преобразование типов контролируется пользователем. Мы должны вызвать специальную функ-
цию.
От целых к действительным:Часто возникает необходимость приведения целых чисел к действитель-
ным при делении. Для этого можно воспользоваться функцией: fromIntegral
Prelude> :i fromIntegral
fromIntegral ::( Integrala, Numb) =>a ->b
-- Defined in ‘GHC.Real’
Определим функцию поиска среднего между двумя целыми числами:
meanInt :: Int -> Int -> Double
meanInt a b =fromIntegral (a +b) /2
Арифметика | 35
В этой функции двойка имеет тип Double. Обратите внимание на скобки: составной синоним всегда при-
тягивает аргументы сильнее чем бинарная операция.
От действительных к целым:В этом нам поможет класс RealFrac. Методы говорят сами за себя:
Prelude GHC.Float> :i RealFrac
class( Reala, Fractionala) => RealFraca where
properFraction :: Integralb =>a ->(b, a)
truncate :: Integralb =>a ->b
round :: Integralb =>a ->b
ceiling :: Integralb =>a ->b
floor :: Integralb =>a ->b
-- Defined in ‘GHC.Real’
instance RealFrac Float-- Defined in ‘GHC.Float’
instance RealFrac Double-- Defined in ‘GHC.Float’
Метод properFraction отделяет целую часть числа от дробной:
properFraction :: Integralb =>a ->(b, a)
Для того, чтобы вернуть сразу два значения используется кортеж (кортежи пишутся в обычных скобках,
значения следуют через запятую):
Prelude>properFraction 2.5
(2,0.5)
Для пар (кортеж, состоящий из двух элементов) определены две удобные функции извлечения элементов,
их смысл можно понять по одним лишь типам:
fst ::(a, b) ->a
snd ::(a, b) ->b
Проверим:
Prelude> letx =properFraction 2.5
Prelude>(fst x, snd x)
(2, 0.5)
Мы бы и сами могли определить такие функции:
fst ::(a, b) ->a
fst (a, _) =a
snd ::(a, b) ->b
snd ( _, b) =b
Между действительными числами:Кто-то написал очень хорошую функцию, но она определена на
Double, а вам приходится использовать Float. Как быть? Нам поможет функция realToFrac:
Prelude> :i realToFrac
realToFrac ::( Reala, Fractionalb) =>a ->b
-- Defined in ‘GHC.Real’
Она принимает значение из класса Realи приводит его к значению, которое можно делить. Что это за
класс Real? Математики наверное смекнут, что это противоположность комплексным числам (где-то должен
быть определён тип или класс Complex, и он правда есть, но об этом в следующем разделе). При переходе
Читать дальше