Следующая наша миссия – написание функции, которая принимает на вход два списка и сообщает, содержится ли первый список целиком где-нибудь внутри второго. Например, список >[3,4]
содержится внутри >[1,2,3,4,5]
, а вот >[2,5]
– уже нет. Список, в котором мы ищем, назовём стогом, а список, который хотим обнаружить, – иголкой.
Чтобы выбраться из этой передряги, воспользуемся функцией >tails
из того же модуля >Data.List
. Она принимает список и последовательно применяет к нему функцию >tail
. Вот пример:
>ghci> tails "победа"
>["победа","обеда","беда","еда","да","а",""]
>ghci> tails [1,2,3]
>[[1,2,3],[2,3],[3],[]]
Возможно, пока неясно, зачем она нам вообще понадобилась. Сейчас увидим.
Предположим, мы хотим найти строку >"обед"
внутри строки >"победа"
. Для начала вызовем >tails
и получим все «хвосты» списка. Затем присмотримся к каждому «хвосту», и если хоть какой-нибудь из них начинается со строки >"обед"
, значит, иголка найдена! Вот если бы мы искали >"фуу"
внутри >"победа"
– тогда да, ни один из «хвостов» с >"фуу"
не начинается.
Чтобы узнать, не начинается ли одна строка с другой, мы применим функцию >isPrefixOf
, снова из модуля >Data.List
. Ей на вход подаются два списка, а она отвечает, начинается второй список с первого или нет.
>ghci> "гавайи" `isPrefixOf` "гавайи джо"
>True
>ghci> "ха-ха" `isPrefixOf` "ха"
>False
>ghci> "ха" `isPrefixOf` "ха"
>True
Теперь нужно лишь проверить, начинается ли какой-нибудь из хвостов нашего стога с иголки. Тут поможет функция >any
из >Data.
>List
. Она принимает предикат и список и сообщает, удовлетворяет ли этому предикату хотя бы один элемент списка. Внимание:
>ghci> any (>4) [1,2,3]
>False
>ghci> any (=='Х') "Грегори Хаус"
>True
>ghci> any (\x -> x > 5 && x < 10) [1,4,11]
>False
Соберём все эти функции вместе:
>import Data.List
>isIn :: (Eq a) => [a] -> [a] -> Bool
>needle `isIn` haystack = any (needle `isPrefixOf`) (tails haystack)
Вот и всё! Функция >tails
создаёт список «хвостов» нашего стога, а затем мы смотрим, начинается ли что-нибудь из них с иголки. Проверим:
>ghci> "обед" `isIn` "победа"
>True
>ghci> [1,2] `isIn` [1,3,5]
>False
Ой, подождите-ка! Кажется, только что написанная функция уже есть в >Data.List
… Ба-а, и правда! Она называется >isInfixOf
и делает в точности то же, что наша >isIn
.