Изучай Haskell во имя добра! (Липовача) - страница 276

, мы записываем его так, как будто это дробь. Числитель и знаменатель разделяются символом >%. Вот несколько примеров:

>ghci> 1 % 4

>1 % 4

>ghci> 1 % 2 + 1 % 2

>1 % 1

>ghci> 1 % 3 + 5 % 4

>19 % 12

Первая строка – это просто одна четвёртая. Во второй строке мы складываем две половины, чтобы получить целое. В третьей строке складываем одну третью с пятью четвёртыми и получаем девять двенадцатых. Поэтому давайте выбросим эти плавающие запятые и используем для наших вероятностей тип >Rational:

>ghci> [(3,1 % 2),(5,1 % 4),(9,1 % 4)]

>[(3,1 % 2),(5,1 % 4),(9,1 % 4)]

Итак, >3 имеет один из двух шансов появиться, тогда как >5 и >9 появляются один раз из четырёх. Просто великолепно!

Мы взяли списки и добавили к ним некоторый дополнительный контекст, так что это тоже представляет значения с контекстами. Прежде чем пойти дальше, давайте обернём это в >newtype, ибо, как я подозреваю, мы будем создавать некоторые экземпляры.

>import Data.Ratio


>newtype Prob a = Prob { getProb :: [(a, Rational)] } deriving Show

Это функтор?.. Ну, раз список является функтором, это тоже должно быть функтором, поскольку мы только что добавили что-то в список. Когда мы отображаем список с помощью функции, то применяем её к каждому элементу. Тут мы тоже применим её к каждому элементу, но оставим вероятности как есть. Давайте создадим экземпляр:

>instance Functor Prob where

>   fmap f (Prob xs) = Prob $ map (\(x, p) –> (f x, p)) xs

Мы разворачиваем его из >newtype при помощи сопоставления с образцом, затем применяем к значениям функцию >f, сохраняя вероятности как есть, и оборачиваем его обратно. Давайте посмотрим, работает ли это:

>ghci> fmap negate (Prob [(3,1 % 2),(5,1 % 4),(9,1 % 4)])

>Prob {getProb = [(-3,1 % 2),(-5,1 % 4),(-9,1 % 4)]}

Обратите внимание, что вероятности должны давать в сумме 1. Если все эти вещи могут случиться, не имеет смысла, чтобы сумма их вероятностей была чем-то отличным от 1. Думаю, выпадение монеты на решку 75% раз и на орла 50% раз могло бы происходить только в какой-то странной Вселенной.

А теперь главный вопрос: это монада? Учитывая, что список является монадой, похоже, и это должно быть монадой. Во-первых, давайте подумаем о функции >return. Как она работает со списками? Она берёт значение и помещает его в одноэлементный список. Что здесь происходит? Поскольку это должен быть минимальный контекст по умолчанию, она тоже должна создавать одноэлементный список. Что же насчёт вероятности? Вызов выражения >return x должен создавать монадическое значение, которое всегда представляет >x как свой результат, поэтому не имеет смысла, чтобы вероятность была равна