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

Например мы можем сделать тип для описания паспорта:

data Passport

= Person {

surname

:: String,

-- Фамилия

givenName

:: String,

-- Имя

nationality

:: String,

-- Национальность

dateOfBirth

:: Date,

-- Дата рождения

sex

:: Bool,

-- Пол

placeOfBirth

:: String,

-- Место рождения

authority

:: String,

-- Место выдачи документа

dateOfIssue

:: Date,

-- Дата выдачи

dateOfExpiry

:: Date

-- Дата окончания срока

} deriving (Eq, Show)

--

действия

data Date

= Date {

day

:: Int,

month

:: Int,

year

:: Int

} deriving (Show, Eq)

В фигурных скобках через запятую мы указываем поля. Поле состоит из имени и типа. Теперь нам до-

ступны две операции:

• Чтение полей

hello :: Passport -> String

hello p = ”Hello, ” ++ givenName p ++ ”!”

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

Для чтения мы просто подставляем в имя поля данное значение. В этой функции мы приветствуем

человека и обращаемся к нему по имени. Для того, чтобы узнать его имя мы подсмотрели в паспорт, в

поле givenName.

• Обновление полей. Для обновления полей мы пользуемся таким синтаксисом:

value { fieldName1 = newValue1, fieldName2 = newValue2, ... }

Мы присваиваем в значении value полю с именем fieldName новое значение newFieldValue. К примеру

продлим срок действия паспорта на десять лет:

prolongate :: Passport -> Passport

prolongate p = p{ dateOfExpiry = newDate }

where newDate = oldDate { year = year oldDate + 10 }

oldDate = dateOfExpiry p

Вернёмся к типам Sum и Prod:

newtype Sum

a = Sum

{ getSum

:: a }

newtype Prod a = Prod { getProd :: a }

Этой записью мы определили два типа-обёртки. У нас есть две функции, которые заворачивают обычное

значение, это Sum и Prod. С помощью записей мы тут же в определении типа определили функции которые

разворачивают значения, это getSum и getProd.

Вспомним определение для типа State:

data State s a = State (s -> (a, s))

runState :: State s a -> (s -> (a, s))

runState (State f) = f

Было бы гораздо лучше определить его так:

newtype State s a = State{ runState :: s -> (a, s) }

Накопление чисел

Но вернёмся к нашей задаче. Мы будем накапливать сумму в значении типа Sum. Поскольку нас интере-

сует лишь значение накопителя, наша функция будет возвращать значение единичного типа ().

countBiFuns :: Exp -> Int

countBiFuns = getSum . execWriter . countBiFuns’

countBiFuns’ :: Exp -> Writer (Sum Int) ()

countBiFuns’ x = case x of

Add a b -> tell (Sum 1) *> bi a b

Mul a b -> tell (Sum 1) *> bi a b

Neg a

-> un a

_

-> pure ()

where bi a b = countBiFuns’ a *> countBiFuns’ b

un

= countBiFuns’

tell :: Monoid a => a -> Writer a ()

tell a = Writer ((), a)

execWriter :: Writer msg a -> msg

execWriter (Writer (a, msg)) = msg

Первая функция countBiFuns извлекает значение из типов Writer и Sum. А вторая функция countBiFuns’

вычисляет значение.

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

execWriter, которая возвращает лишь сообщение. Это стандартные для Writer функции.

Посмотрим как работает эта функция:

*Exp> countBiFuns (n 2)

0

*Exp> countBiFuns (n 2 + n 1 + 2 + 3)

3

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

Накопление логических значений

В модуле Data.Monoid определены два типа для накопления логических значений. Это типы All и Any. С

помощью типа All мы можем проверить выполняется ли некоторое свойство для всех значений. А с помощью

типа Any мы можем узнать, что существует хотя бы один элемент, для которых это свойство выполнено.

Посмотрим на определение экземпляров класса Monoid для этих типов:

newtype All = All { getAll :: Bool }

instance Monoid All where

mempty = All True

All x ‘mappend‘ All y = All (x && y)

В типе All мы накапливаем значения с помощью логического “и”. Нейтральным элементом является кон-

структор True. Итоговое значение накопителя будет равно True только в том случае, если все накапливаемые

сообщения были равны True.

В типе Any всё наоборот:

instance Monoid Any where

mempty = Any False

Any x ‘mappend‘ Any y = Any (x || y)

Посмотрим как работают эти типы. Составим функцию, которая проверяет отсутствие оператора минус

в выражении:

noNeg :: Exp -> Bool

noNeg = not . getAny . execWriter . anyNeg

anyNeg :: Exp -> Writer Any ()

anyNeg x = case x of

Neg _

-> tell (Any True)

Add a b -> bi a b

Mul a b -> bi a b

_

-> pure ()

where bi a b = anyNeg a *> anyNeg b

Функция anyNeg проверяет есть ли в выражении хотя бы один конструктор Neg. В функции noNeg мы

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

одного конструктора Neg.

*Exp> noNeg (n 2 + n 1 + 2 + 3)

True

*Exp> noNeg (n 2 - n 1 + 2 + 3)

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

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

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

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

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

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

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

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

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