Kniga-Online.club
» » » » Миран Липовача - Изучай Haskell во имя добра!

Миран Липовача - Изучай Haskell во имя добра!

Читать бесплатно Миран Липовача - Изучай Haskell во имя добра!. Жанр: Программирование издательство -, год 2004. Так же читаем полные версии (весь текст) онлайн без регистрации и SMS на сайте kniga-online.club или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Перейти на страницу:

landLeft :: Birds –> Pole –> Maybe Pole

landLeft n (left,right)

   | abs ((left + n) - right) < 4 = Just (left + n, right)

   | otherwise                    = Nothing

landRight :: Birds –> Pole –> Maybe Pole

landRight n (left,right)

   | abs (left - (right + n)) < 4 = Just (left, right + n)

   | otherwise                    = Nothing

Вместо того чтобы вернуть значение типа Pole, эти функции теперь возвращают значения типа Maybe Pole. Они по-прежнему принимают количество птиц и прежний шест, как и ранее, но затем проверяют, выведет ли Пьера из равновесия приземление такого количества птиц. Мы используем охранные выражения, чтобы проверить, меньше ли разница в количестве птиц на новом шесте, чем 4. Если меньше, оборачиваем новый шест в конструктор Just и возвращаем это. Если не меньше, возвращаем значение Nothing, сигнализируя о неудаче.

Давайте опробуем этих деток:

ghci> landLeft 2 (0, 0)

Just (2,0)

ghci> landLeft 10 (0, 3)

Nothing

Когда мы приземляем птиц, не выводя Пьера из равновесия, мы получаем новый шест, обёрнутый в конструктор Just. Но когда значительное количество птиц в итоге оказывается на одной стороне шеста, в результате мы получаем значение Nothing. Всё это здорово, но, похоже, мы потеряли возможность многократного приземления птиц на шесте! Выполнить landLeft 1 (landRight 1 (0, 0)) больше нельзя, потому что когда landRight 1 применяется к (0, 0), мы получаем значение не типа Pole, а типа Maybe Pole. Функция landLeft 1 принимает параметр типа Pole, а не Maybe Pole.

Нам нужен способ получения Maybe Pole и передачи его функции, которая принимает Pole и возвращает Maybe Pole. К счастью, у нас есть операция >>=, которая делает именно это для типа Maybe. Давайте попробуем:

ghci> landRight 1 (0, 0) >>= landLeft 2

Just (2,1)

Вспомните, что функция landLeft 2 имеет тип Pole –> Maybe Pole. Мы не можем просто передать ей значение типа Maybe Pole, которое является результатом вызова функции landRight 1 (0, 0), поэтому используем операцию >>=, чтобы взять это значение с контекстом и отдать его функции landLeft 2. Операция >>= действительно позволяет нам обрабатывать значения типа Maybe как значения с контекстом. Если мы передадим значение Nothing в функцию landLeft 2, результатом будет Nothing, и неудача будет распространена:

ghci> Nothing >>= landLeft 2

Nothing

Используя это, мы теперь можем помещать в цепочку приземления, которые могут окончиться неуспешно, потому что оператор >>= позволяет нам передавать монадическое значение функции, которая принимает обычное значение. Вот последовательность приземлений птиц:

ghci> return (0, 0) >>= landRight 2 >>= landLeft 2 >>= landRight 2

Just (2,4)

Вначале мы использовали функцию return, чтобы взять шест и обернуть его в конструктор Just. Мы могли бы просто применить выражение landRight 2 к значению (0, 0) – это было бы то же самое, – но так можно добиться большего единообразия, используя оператор >>= для каждой функции. Выражение Just (0, 0) передаётся в функцию landRight 2, что в результате даёт результат Just (0, 2). Это значение в свою очередь передаётся в функцию landLeft 2, что в результате даёт новый результат (2, 2), и т. д.

Помните следующий пример, прежде чем мы ввели возможность неудачи в инструкции Пьера?

ghci> (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)

(0,2)

Он не очень хорошо симулировал взаимодействие канатоходца с птицами. В середине его равновесие было нарушено, но результат этого не отразил. Давайте теперь исправим это, используя монадическое применение (оператор >>=) вместо обычного:

ghci> return (0, 0) >>= landLeft 1 >>= landRight 4 >>= landLeft (-1) >>= landRight (-2)

Nothing

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

1. Функция return помещает значение (0, 0) в контекст по умолчанию, превращая значение в Just (0, 0).

2. Происходит вызов выражения Just (0, 0) >>= landLeft 1. Поскольку значение Just (0, 0) является значением Just, функция landLeft 1 применяется к (0, 0), что в результате даёт результат Just (1, 0), потому что птицы всё ещё находятся в относительном равновесии.

3. Имеет место вызов выражения Just (1, 0) >>= landRight 4, и результатом является выражение Just (1, 4), поскольку равновесие птиц пока ещё не затронуто, хотя Пьер уже удерживается с трудом.

4. Выражение Just (1, 4) передаётся в функцию landLeft (–1). Это означает, что имеет место вызов landLeft (–1) (1, 4). Теперь ввиду особенностей работы функции landLeft в результате это даёт значение Nothing, так как результирующий шест вышел из равновесия.

5. Теперь, поскольку у нас есть значение Nothing, оно передаётся в функцию landRight (–2), но так как это Nothing, результатом автоматически становится Nothing, поскольку нам не к чему применить эту функцию.

Мы не смогли бы достигнуть этого, просто используя Maybe в качестве аппликативного функтора. Если вы попробуете так сделать, то застрянете, поскольку аппликативные функторы не очень-то позволяют аппликативным значениям взаимодействовать друг с другом. Их в лучшем случае можно использовать как параметры для функции, применяя аппликативный стиль.

Аппликативные операторы извлекут свои результаты и передадут их функции в соответствующем для каждого аппликативного функтора виде, а затем соберут окончательное аппликативное значение, но взаимодействие между ними не особенно заметно. Здесь, однако, каждый шаг зависит от результата предыдущего шага. Во время каждого приземления возможный результат предыдущего шага исследуется, а шест проверяется на равновесие. Это определяет, окончится ли посадка успешно либо неуспешно.

Банан на канате

Давайте разработаем функцию, которая игнорирует текущее количество птиц на балансировочном шесте и просто заставляет Пьера поскользнуться и упасть. Мы назовём её banana:

banana :: Pole –> Maybe Pole

banana _ = Nothing

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

ghci> return (0, 0) >>= landLeft 1 >>= banana >>= landRight 1

Nothing

Функции banana передаётся значение Just (1, 0), но она всегда производит значение Nothing, которое заставляет всё выражение возвращать в результате Nothing. Какая досада!..

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

(>>) :: (Monad m) => m a –> m b –> m b

m >> n = m >>= _ –> n

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

ghci> Nothing >> Just 3

Nothing

ghci> Just 3 >> Just 4

Just 4

ghci> Just 3 >> Nothing

Nothing

Если мы заменим оператор >> на вызов >>= _ –>, легко увидеть, что происходит.

Мы можем заменить нашу функцию banana в цепочке на оператор >> и следующее за ним значение Nothing, чтобы получить гарантированную и очевидную неудачу:

ghci> return (0, 0) >>= landLeft 1 >> Nothing >>= landRight 1

Nothing

Как бы это выглядело, если бы мы не сделали разумный выбор, обработав значения типа Maybe как значения с контекстом неудачи и передав их функциям? Вот какой была бы последовательность приземлений птиц:

routine :: Maybe Pole

routine = case landLeft 1 (0, 0) of

   Nothing –> Nothing

   Just pole1 –> case landRight 4 pole1 of

      Nothing –> Nothing

      Just pole2 –> case landLeft 2 pole2 of

         Nothing –> Nothing

         Just pole3 –> landLeft 1 pole3

Мы усаживаем птицу слева, а затем проверяем вероятность неудачи и вероятность успеха. В случае неудачи мы возвращаем значение Nothing. В случае успеха усаживаем птиц справа, а затем повторяем всё сызнова. Превращение этого убожества в симпатичную цепочку монадических применений с использованием функции >>= является классическим примером того, как монада Maybe экономит массу времени, когда вам необходимо последовательно выполнить вычисления, основанные на вычислениях, которые могли окончиться неуспешно.

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

Миран Липовача читать все книги автора по порядку

Миран Липовача - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки kniga-online.club.


Изучай Haskell во имя добра! отзывы

Отзывы читателей о книге Изучай Haskell во имя добра!, автор: Миран Липовача. Читайте комментарии и мнения людей о произведении.


Уважаемые читатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор kniga-online.


Прокомментировать
Подтвердите что вы не робот:*
Подтвердите что вы не робот:*