из модуля
>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)))