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

истинно, то элемент добавляется к результирующему списку. Если нет – элемент пропускается.

Несколько примеров:

>ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1]

>[5,6,4]

>ghci> filter (==3) [1,2,3,4,5]

>[3]

>ghci> filter even [1..10]

>[2,4,6,8,10]

>ghci> let notNull x = not (null x) in filter notNull [[1],[],[3,4],[]]

>[[1],[3,4]]

>ghci> filter (`elem` ['а'..'я']) "тЫ СМЕЕШЬСя, ВЕДЬ я ДрУГой"

>"тяярой"

>ghci> filter (`elem` ['А'..'Я']) "я Смеюсь, Ведь ты такОЙ же"

>"СВОЙ"

Того же самого результата можно достичь, используя генераторы списков и предикаты. Нет какого-либо правила, диктующего вам, когда использовать функции >map и >filter, а когда – генераторы списков. Вы должны решить, что будет более читаемым, основываясь на коде и контексте. В генераторах списков можно применять несколько предикатов; при использовании функции >filter придётся проводить фильтрацию несколько раз или объединять предикаты с помощью логической функции >&&. Вот пример:

>ghci> filter (<15) (filter even [1..20])

>[2,4,6,8,10,12,14]

Здесь мы берём список >[1..20] и фильтруем его так, чтобы остались только чётные числа. Затем список передаётся функции >filter (<15), которая избавляет нас от чисел >15 и больше. Вот версия с генератором списка:

>ghci> [ x | x <- [1..20], x < 15, even x]

>[2,4,6,8,10,12,14]

Мы используем генератор для извлечения элементов из списка >[1..20], а затем указываем условия, которым должны удовлетворять элементы результирующего списка.

Помните нашу функцию быстрой сортировки (см. предыдущую главу, раздел «Сортируем, быстро!»)? Мы использовали генераторы списков для фильтрации элементов меньших (или равных) и больших, чем опорный элемент. Той же функциональности можно добиться и более понятным способом, используя функцию >filter:

>quicksort :: (Ord a) => [a] –> [a]

>quicksort [] = []

>quicksort (x:xs) =

>  let smallerSorted = quicksort (filter (<= x) xs)

>      biggerSorted = quicksort (filter (> x) xs)

>  in  smallerSorted ++ [x] ++ biggerSorted

Ещё немного примеров использования map и filter

Давайте найдём наибольшее число меньше 100 000, которое делится на число 3829 без остатка. Для этого отфильтруем множество возможных вариантов, в которых, как мы знаем, есть решение.

>largestDivisible :: Integer

>largestDivisible = head (filter p [100000,99999..])

>  where p x = x `mod` 3829 == 0

Для начала мы создали список всех чисел меньших 100 000 в порядке убывания. Затем отфильтровали список с помощью предиката. Поскольку числа отсортированы в убывающем порядке, наибольшее из них, удовлетворяющее предикату, будет первым элементом отфильтрованного списка. Нам даже не нужно использовать конечный список для нашего базового множества. Снова «лень в действии»! Поскольку мы используем только «голову» списка, нам неважно, конечен полученный список или бесконечен. Вычисления прекращаются, как только находится первое подходящее решение.