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

», мы знаем, в каком порядке должны следовать эти слова. Чтобы лучше понять, как >EQ является единичным значением, обратите внимание, что если бы мы втиснули одну и ту же букву в одну и ту же позицию в обоих словах, их расположение друг относительно друга в алфавитном порядке осталось бы неизменным; к примеру, слово «oix» будет по-прежнему идти следом за «oin».

Важно, что в экземпляре класса >Monoid для типа >Ordering выражение >x `mappend` y не равно выражению >y `mappend` x. Поскольку первый параметр сохраняется, если он не равен >EQ, >LT `mappend` GT в результате вернёт >LT, тогда как >GT `mappend` LT в результате вернёт >GT:

>ghci> LT `mappend` GT

>LT

>ghci> GT `mappend` LT

>GT

>ghci> mempty `mappend` LT

>LT

>ghci> mempty `mappend` GT

>GT

Хорошо, так чем же этот моноид полезен? Предположим, мы пишем функцию, которая принимает две строки, сравнивает их длину и возвращает значение типа >Ordering. Но если строки имеют одинаковую длину, то вместо того, чтобы сразу вернуть значение >EQ, мы хотим установить их расположение в алфавитном порядке.

Вот один из способов это записать:

>lengthCompare :: String –> String –> Ordering

>lengthCompare x y = let a = length x `compare` length y

>                        b = x `compare` y

>                     in if a == EQ then b else a

Результат сравнения длин мы присваиваем образцу >a, результат сравнения по алфавиту – образцу >b; затем, если оказывается, что длины равны, возвращаем их порядок по алфавиту.

Но, имея представление о том, что тип >Ordering является моноидом, мы можем переписать эту функцию в более простом виде:

>import Data.Monoid


>lengthCompare :: String –> String –> Ordering

>lengthCompare x y = (length x `compare` length y) `mappend`(x `compare` y)

Давайте это опробуем:

>ghci> lengthCompare "ямб" "хорей"

>LT

>ghci> lengthCompare "ямб" "хор"

>GT

Вспомните, что когда мы используем функцию >mappend, сохраняется её левый параметр, если он не равен значению >EQ; если он равен >EQ, сохраняется правый. Вот почему мы поместили сравнение, которое мы считаем первым, более важным критерием, в качестве первого параметра. Теперь предположим, что мы хотим расширить эту функцию, чтобы она также сравнивала количество гласных звуков, и установить это вторым по важности критерием для сравнения. Мы изменяем её вот так:

>import Data.Monoid


>lengthCompare :: String –> String –> Ordering

>lengthCompare x y = (length x `compare` length y) `mappend`

>                    (vowels x `compare` vowels y) `mappend`

>                    (x `compare` y)

>   where vowels = length . filter (`elem` "аеёиоуыэюя")