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

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