Хитрость заключается в том, чтобы вытащить два элементов из списка ввода типовых сопоставлений двух минусов клеток. Если первый элемент проходит предикат, мы будем придерживаться второго на выходе. Но не забудьте вставить второй элемент обратно в список ввода, когда вы делаете рекурсивный вызов.
afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter f [] = [] -- input list is empty
afterFilter f [x] = [] -- input list has only one element - no "next element" to return
afterFilter f (x:y:xs) =
let ys = afterFilter f (y:xs)
in (if f x then y:ys else rest)
Однако, более высокого уровня - и многое другое Haskellish - способ подойти к проблеме было бы разбить его в трубопровод операций.
- пара на каждый элемент в списке с элементом, который следует за ним с помощью
zip
, поэтому у нас есть список (element, next)
пар.
- Используйте
filter
, чтобы сбросить пары, для которых element
не передает предикат.
- Используйте
map
для извлечения next
части каждой оставшейся пары.
Так что код выглядит следующим образом:
pairWithSuccessors :: [a] -> [(a, a)]
pairWithSuccessors xs = zip xs (tail xs)
afterFilter :: (a -> Bool) -> [a] -> [a]
afterFilter p xs =
let withSuccessors = pairWithSuccessors xs (tail xs)
filtered = filter (\(element, next) -> p element) withSuccessors
filteredSuccessors = map (\(element, next) -> next) filtered
in filteredSuccessors
Или, написанным в точечного свободного стиля:
afterFilter p = map snd . filter (p . fst) . pairWithSuccessors
функции, построенной с оператором состава.
считывается прямо -to-left: сначала pairWithSuccessors
, затем filter (p . fst)
, затем map snd
над t он результат.
GHC хорош при работе со списками: при компиляции с оптимизациями, оба подход должен производить примерно тот же машинный код - то есть, нет никакой потери производительности для решения высокого уровня
Вы отказались от лучшей части (IMO) безостановочного решения! 'afterFilter p = map snd. фильтр (стр. fst). (zip <*> хвост) ' – chepner