, мы записываем его так, как будто это дробь. Числитель и знаменатель разделяются символом
>%
. Вот несколько примеров:
>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
как свой результат, поэтому не имеет смысла, чтобы вероятность была равна