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

, чтобы создать список, который содержит >x копий функции >moveKnight. Затем мы монадически компонуем все эти функции в одну, что даёт нам функцию, которая берёт исходную позицию и недетерминированно перемещает коня >x раз. Потом просто превращаем исходную позицию в одноэлементный список с помощью функции >return и передаём его исходной функции.

Теперь нашу функцию >canReachIn3 тоже можно сделать более общей:

>canReachIn :: Int –> KnightPos –> KnightPos –> Bool

>canReachIn x start end = end `elem` inMany x start

Создание монад


В этом разделе мы рассмотрим пример, показывающий, как тип создаётся, опознаётся как монада, а затем для него создаётся подходящий экземпляр класса >Monad. Обычно мы не намерены создавать монаду с единственной целью – создать монаду. Наоборот, мы создаём тип, цель которого – моделировать аспект некоторой проблемы, а затем, если впоследствии мы видим, что этот тип представляет значение с контекстом и может действовать как монада, мы определяем для него экземпляр класса >Monad.

Как вы видели, списки используются для представления недетерминированных значений. Список вроде >[3,5,9] можно рассматривать как одно недетерминированное значение, которое просто не может решить, чем оно будет. Когда мы передаём список в функцию с помощью операции >>>=, это просто создаёт все возможные варианты получения элемента из списка и применения к нему функции, а затем представляет эти результаты также в списке.

Если мы посмотрим на список >[3,5,9] как на числа >3, >5, и >9, встречающиеся одновременно, то можем заметить, что нет никакой информации в отношении того, какова вероятность встретить каждое из этих чисел. Что если бы нам было нужно смоделировать недетерминированное значение вроде >[3,5,9], но при этом мы бы хотели показать, что >3 имеет 50-процентный шанс появиться, а вероятность появления >5 и >9 равна 25%? Давайте попробуем провести эту работу!

Скажем, что к каждому элементу списка прилагается ещё одно значение: вероятность того, что он появится. Имело бы смысл представить это значение вот так:

>[(3,0.5),(5,0.25),(9,0.25)]

Вероятности в математике обычно выражают не в процентах, а в вещественных числах между 0 и 1. Значение 0 означает, что чему-то ну никак не суждено сбыться, а значение 1 – что это что-то непременно произойдёт. Числа с плавающей запятой могут быстро создать путаницу, потому что они стремятся к потере точности, но язык Haskell предлагает тип данных для вещественных чисел. Он называется >Rational, и определён он в модуле >Data.Ratio. Чтобы создать значение типа