Когда мы применяем функцию landLeft 1
(0, 0)
, у нас получается результат (1, 0)
. Затем мы усаживаем птицу на правой стороне, что даёт в результате (1, 1)
. Наконец, две птицы приземляются на левой стороне, что даёт в результате (3, 1)
. Мы применяем функцию к чему-либо, сначала записывая функцию, а затем её параметр, но здесь было бы лучше, если бы первым шел шест, а потом функция посадки. Предположим, мы создали вот такую функцию:x -: f = f x
Можно применять функции, сначала записывая параметр, а затем функцию:
ghci> 100 -: (*3)
300
ghci> True -: not
False
ghci> (0, 0) -: landLeft 2
(2,0)
Используя эту форму, мы можем многократно производить приземление птиц на шест в более «читабельном» виде:
ghci> (0, 0) -: landLeft 1 -: landRight 1 -: landLeft 2
(3,1)
Круто!.. Эта версия эквивалентна предыдущей, где мы многократно усаживали птиц на шест, но выглядит она яснее. Здесь очевиднее, что мы начинаем с (0, 0)
Я улечу
Пока всё идёт нормально, но что произойдёт, если десять птиц приземлятся на одной стороне?
ghci> landLeft 10 (0, 3)
(10,3)
Десять птиц с левой стороны и лишь три с правой?! Этого достаточно, чтобы отправить в полёт самого Пьера!.. Довольно очевидная вещь. Но что если бы у нас была примерно такая последовательность посадок:
ghci> (0, 0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
(0,2)
Может показаться, что всё хорошо, но если вы проследите за шагами, то увидите, что на правой стороне одновременно находятся четыре птицы – а на левой ни одной! Чтобы исправить это, мы должны ещё раз взглянуть на наши функции landLeft
landRight
.Необходимо дать функциям landLeft
landRight
возможность завершаться неуспешно. Нам нужно, чтобы они возвращали новый шест, если равновесие поддерживается, но завершались неуспешно, если птицы приземляются неравномерно. И какой способ лучше подойдёт для добавления к значению контекста неудачи, чем использование типа Maybe
? Давайте переработаем эти функции: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)