haskell-notes

тягивает аргументы сильнее чем бинарная операция.

От действительных к целым: В этом нам поможет класс RealFrac. Методы говорят сами за себя:

Prelude GHC.Float> :i RealFrac

class (Real a, Fractional a) => RealFrac a where

properFraction :: Integral b => a -> (b, a)

truncate :: Integral b => a -> b

round :: Integral b => a -> b

ceiling :: Integral b => a -> b

floor :: Integral b => a -> b

— Defined in ‘GHC.Real’

instance RealFrac Float — Defined in ‘GHC.Float’

instance RealFrac Double — Defined in ‘GHC.Float’

Метод properFraction отделяет целую часть числа от дробной:

properFraction :: Integral b => a -> (b, a)

Для того, чтобы вернуть сразу два значения используется кортеж (кортежи пишутся в обычных скобках,

значения следуют через запятую):

Prelude> properFraction 2.5

(2,0.5)

Для пар (кортеж, состоящий из двух элементов) определены две удобные функции извлечения элементов,

их смысл можно понять по одним лишь типам:

fst :: (a, b) -> a

snd :: (a, b) -> b

Проверим:

Prelude> let x = 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 :: (Real a, Fractional b) => a -> b

— Defined in ‘GHC.Real’

Она принимает значение из класса Real и приводит его к значению, которое можно делить. Что это за

класс Real? Математики наверное смекнут, что это противоположность комплексным числам (где-то должен

быть определён тип или класс Complex, и он правда есть, но об этом в следующем разделе). При переходе

к комплексным числам мы теряем способность сравнения на больше/меньше, но сохраняем возможность

вычисления арифметических операций, поэтому класс Real это пересечение классов Num и Ord:

Prelude> :i Real

class (Num a, Ord a) => Real a where

toRational :: a -> Rational

Здесь “пересечение” означает “и тот и другой”. Пересечение классов кодируется с помощью контекста.

Вернёмся к нашему первому примеру:

36 | Глава 2: Первая программа

Prelude> realToFrac (1::Float) + (1::Double)

2.0

Отметим, что этой функцией можно пользоваться не только для типов Float и Double, в Haskell возможны

самые экзотические числа.

Если преобразования между Float и Double происходят очень-очень часто, возможно имеет смысл вос-

пользоваться специальными для GHC функциями: Они определены в модуле GHC.Float:

Prelude> :m +GHC.Float

Prelude GHC.Float> :t float2Double

float2Double :: Float -> Double

Prelude GHC.Float> :t double2float

double2Float :: Double -> Float

2.7 Документация

К этой главе мы уже рассмотрели основные конструкции языка и базовые типы. Если у вас есть какая-то

задача, вы уже можете начать её решать. Для этого сначала нужно будет описать в типах проблему, затем

выразить с помощью функций её решение.

Но не стоит писать все функции самостоятельно, если функция достаточно общая её наверняка кто-

нибудь уже написал. Самые полезные функции и классы определены в модуле Prelude и основных стан-

дартных библиотечных модулях. Было бы излишним описывать каждую функцию, книга превратилась бы

в справочник. Вместо этого давайте научимся искать функции в документации. Нам понадобится умение

составлять типы функций и небольшое знание английского языка.

Для начала о том, где находится документация к стандартным модулям. Если вы установили ghc вме-

сте с Haskell Platform под Windows скорее всего во вкладке Пуск, там где иконка ghc там же находится

и документация. В Linux необходимо найти директорию с документацией, скорее всего она в директории

/usr/local/share/doc/ghc/libraries. Также документацию можно найти в интернете, наберите в поиско-

вике Haskell Hierarchical Libraries. На главной странице документации вы найдёте огромное количество мо-

дулей. Нас пока интересуют разделы Data и Prelude. Разделы расположены по алфавиту. То что вы видите

это стандартный вид документации в Haskell. Документация делается с помощью специального приложе-

ния Haddock, мы тоже научимся такие делать, но позже, пока мы попробуем разобраться с тем как искать в

документации функции.

Предположим нам нужно вычислить длину списка. Нам нужна функция, которая принимает список и

возвращает целое число, скорее всего её тип [a] -> Int, обычно во всех библиотечных функциях для це-

лых чисел используется тип Int, также на месте параметра используются буквы a, b, c. Мы можем открыть

документацию к Prelude набрать в строке поиска тип [a] -> Int. Или поискать такую функцию в разде-

ле функций для списков List Operations. Тогда мы увидим единственную функцию с таким типом, под

говорящим именем length. Так мы нашли то, что искали.

Или мы ищем функцию, которая переворачивает список, нам нужна функция с типом [a] -> [a]. Таких

функций в Prelude несколько, но имя reverse одной из них может намекнуть на её смысл.

Но одной Prelude мир стандартных функций Haskell не ограничивается, если вы не нашли необходимую

вам функцию в Prelude её стоит поискать в других библиотечных модулях. Обычно функции разделяются

по тому на каких типах они определены. Так например функция sort :: Ord a => [a] -> [a] определена

не в Prelude, а в отдельном библиотечном модуле для списков он называется Data.List. Так же есть много

других модулей для разных типов, таких как Data.Bool, Data.Char, Data.Function, Data.Maybe и многие

другие. Не пугайтесь изобилия модулей постепенно они станут вашей опорой.

Для поиска в стандартных библиотеках есть замечательный интернет-сервис Hoogle (http://www.

haskell.org/hoogle/). Hoogle может искать значения не только по имени, но и по типам. Например мы

хотим узнать целочисленный код символа. Поиск по типу Char -> Int выдаёт искомую функцию digitToInt.

2.8 Краткое содержание

В этой главе мы познакомились с интерпретатором ghci и основными типами. Рассмотрели много при-

меров.

Документация | 37

Типы

Bool

– Основные операции: &&, ||, not, if c then t else e

Char

– Значения пишутся в ординарных кавычках, как в ’H’, ’+’

String

– Значения пишутся в двойных кавычках, как в ”Hello World”

Int

– Эффективные целые числа, но ограниченные

Integer

– Не ограниченные целые числа, но не эффективные

Double

– Числа с двойной точностью

Float

– Числа с ординарной точностью

Rational

– Дробные числа

Нам впервые встретились кортежи (на функции properFraction). Кортежи используются для возвраще-

ния из функции нескольких значений. Элементы кортежа могут иметь разные типы. Для извлечения элемен-

тов из кортежей-пар используются функции fst и snd. Кортежи пишутся в скобках, и элементы разделены

запятыми:

(a, b)

(a, b, c)

(a, b, c, d)

Классы

Show

Печать

Eq

Сравнение на равенство

Num

Сложение и умножение

Fractional

Деление

Особенности синтаксиса

Запись применения функции:

Префиксная

Инфиксная

add a b

a ‘add‘ b

(+) a b

a + b

Также мы научились приводить одни численные типы к другим и пользоваться документацией.

2.9 Упражнения

• Напишите функцию beside :: Nat -> Nat -> Bool, которая будет возвращать True только в том случае,

Читай продолжение на следующей странице
Добавить комментарии