Подсчёт слов
Предположим, что у нас имеется строка, содержащая много слов. Мы хотим выяснить, сколько раз в этой строке встречается каждое слово. Первой функцией, которую мы применим, будет функция words из модуля Data.List. Эта функция преобразует строку в список строк, в котором каждая строка представляет одно слово из исходной строки. Небольшой пример:
ghci> words "всё это слова в этом предложении"
["всё","это","слова","в","этом","предложении"]
ghci> words "всё это слова в этом предложении"
["всё","это","слова","в","этом","предложении"]
Затем воспользуемся функцией group, которая тоже «живёт» в Data.List, чтобы сгруппировать одинаковые слова. Эта функция принимает список и собирает одинаковые подряд идущие элементы в подсписки:
ghci> group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
[[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]]
Но что если одинаковые элементы идут в списке не подряд?
ghci> group ["бум","бип","бип","бум","бум"]
[["бум"],["бип","бип"],["бум","бум"]]
Получаем два списка, содержащих "бум", тогда как нам бы хотелось, чтобы все вхождения одного и того же слова попали в один список. Что делать? Мы можем предварительно отсортировать список! Для этого применим функцию sort из Data.List. Она принимает список элементов, которые могут быть упорядочены, и возвращает новый список, содержащий те же элементы, но упорядоченные от наименьшего к наибольшему:
ghci> sort [5,4,3,7,2,1]
[1,2,3,4,5,7]
ghci> sort ["бум","бип","бип","бум","бум"]
["бип","бип","бум","бум","бум"]
Заметим, что строки упорядочены по алфавиту.
Теперь всё необходимое у нас есть, осталось только записать решение. Берём строку, разбиваем её на список слов, сортируем слова и группируем одинаковые. Затем применяем map и получаем список вроде ("boom", 3); это означает, что слово "boom" встретилось в исходной строке трижды.
import Data.List
wordNums :: String -> [(String, Int)]
wordNums = map (ws -> (head ws, length ws)) . group . sort . words
Для написания этой функции мы применили композицию функций. Предположим, что мы вызвали функцию wordNums для строки "уа уа уи уа". К этой строке применяется функция words, результатом которой будет список ["уа","уа","уи","уа"]. После его сортировки функцией sort получим новый список ["уа","уа","уа","уи"]. Группировка одинаковых подряд идущих слов функцией group даст нам список [["уа","уа","уа"],["уи"]]. Затем с помощью функции map к каждому элементу такого списка (то есть к подсписку) будет применена анонимная функция, которая превращает список в пару – «голова» списка, длина списка. В конечном счёте получаем [("уа",3),("уи",1)].
Вот как можно написать ту же функцию, не пользуясь операцией композиции:
wordNums xs = map (ws -> (head ws, length ws)) (group (sort (words xs)))
Кажется, здесь избыток скобок! Думаю, нетрудно заметить, насколько более читаемой делает функцию операция композиции.