Читаем Учебник по Haskell полностью

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

функций из этого модуля совпадают с именами из модуля Prelude. Теперь все определения из модуля

Data.Map пишутся с приставкой M. .

Создадим вспомогательную функцию, которая упростит вычисление выражений:

runExp :: Exp -> [(String, Int)] -> Int

runExp a env = runReader (eval a) $ M. fromList env

Сохраним определение новых функций в модуле Exp. И посмотрим что у нас получилось:

*Exp> let env a b = [(”1”, a), (”2”, b)]

*Exp> let exp = 2 * (n 1 + n 2) - n 1

*Exp> runExp exp (env 1 2)

5

*Exp> runExp exp (env 10 5)

20

Так мы можем пользоваться функциями с окружением для того, чтобы читать значения из общего ис-

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

монадами:

eval :: Env -> Exp -> Int

eval env x = case x of

Lit n

-> n

Neg n

-> negate $ eval’ n

Add a b

-> eval’ a + eval’ b

Mul a b

-> eval’ a + eval’ b

Var name

-> value env name

where eval’ = eval env

112 | Глава 7: Функторы и монады: примеры

7.4 Накопление результата

Рассмотрим по-подробнее тип Writer. Он выполняет задачу обратную к типу Reader. Когда мы пользова-

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

не извлекать данные из окружения, а записывать их.

Рассмотрим такую задачу нам нужно обойти дерево типа Exp и подсчитать все бинарные операции. Мы

прибавляем к накопителю результата единицу за каждый конструктор Add или Mul. Тип сообщений будет

числом. Нам нужно сделать экземпляр класса Monoid для чисел.

Напомню, что тип накопителя должен быть экземпляром класса Monoid:

class Monoid a where

mempty

:: a

mappend :: a -> a -> a

mconcat :: [a] -> a

mconcat = foldr mappend mempty

Но для чисел возможно несколько вариантов, которые удовлетворяют свойствам. Для сложения:

instance Num a => Monoid a where

mempty

= 0

mappend = (+)

И умножения:

instance Num a => Monoid a where

mempty

= 1

mappend = (*)

Для нашей задачи подойдёт первый вариант, но не исключена возможность того, что для другой зада-

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

проблемы в модуле Data.Monoid определено два типа обёртки:

newtype Sum

a = Sum

{ getSum

:: a }

newtype Prod a = Prod { getProd :: a }

В этом определении есть два новых элемента. Первый это ключевое слово newtype, а второй это фигурные

скобки. Что всё это значит?

Тип-обёртка newtype

Ключевое слово newtype вводит новый тип-обёртку. Тип-обёртка может иметь только один конструктор,

у которого лишь одни аргумент. Запись:

newtype Sum a = Sum a

Это тоже самое, что и

data Sum a = Sum a

Единственное отличие заключается в том, что в случае newtype вычислитель не видит разницы между

Sum a и a. Её видит лишь компилятор. Это означает, что на разворачивание и заворачивание такого значения

в тип обёртку не тратится никаких усилий. Такие типы подходят для решения двух задач:

• Более точная проверка типов.

Например у нас есть типы, которые описывают физические величины, все они являются числами, но у

них также есть и размерности. Мы можем написать:

type Velocity

= Double

type Time

= Double

type Length

= Double

velocity :: Length -> Time -> Velocity

velocity leng time = leng / time

Накопление результата | 113

В этом случае мы спокойно можем подставить на место времени путь и наоборот. Но с помощью типов

обёрток мы можем исключить эти случаи:

newtype Velocity

= Velocity

Double

newtype Time

= Time

Double

newtype Length

= Length

Double

velocity :: Length -> Time -> Velocity

velocity (Length leng) (Time time) = Velocity $ leng / time

В этом случае мы проводим проверку по размерностям, компилятор не допустит смешивания данных.

• Определение нескольких экземпляров одного класса для одного типа. Этот случай мы как раз и рас-

сматриваем для класса Monoid. Нам нужно сделать два экземпляра для одного и того же типа Num a

=> a.

Сделаем две обёртки!

newtype Sum

a = Sum

a

newtype Prod a = Prod a

Тогда мы можем определить два экземпляра для двух разных типов:

Один для Sum:

instance Num a => Monoid (Sum a) where

mempty

= Sum 0

mappend (Sum a) (Sum b) = Sum (a + b)

А другой для Prod:

instance Num a => Monoid (Prod a) where

mempty

= Prod 1

mappend (Prod a) (Prod b) = Prod (a * b)

Записи

Вторая новинка заключалась в фигурных скобках. С помощью фигурных скобок в Haskell обозначаются

записи (records). Запись это произведение типа, но с выделенными именами для полей.

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных