2017-01-07 3 views
3

Я изучаю Haskell в настоящий момент и немного остановился. Я пытаюсь написать функцию, которая принимает предикат p и список xs и возвращает список тех элементов xs, которые сразу же следуют за элементом, который передает предикат p. Вот что у меня есть:предикат и список поиска haskell

afterFilter :: (a -> Bool) -> [a] -> [a] 

afterFilter x (y:ys) = 

    if x y 

     then (map head [ys]) 

    else 

     afterFilter x (tail ys) 

тестового вход: afterFilter (<0) [-4,7,-4,-8,3,-3,-6,0,-9,-1]

выход: [7]

ответ

4

Хитрость заключается в том, чтобы вытащить два элементов из списка ввода типовых сопоставлений двух минусов клеток. Если первый элемент проходит предикат, мы будем придерживаться второго на выходе. Но не забудьте вставить второй элемент обратно в список ввода, когда вы делаете рекурсивный вызов.

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 - способ подойти к проблеме было бы разбить его в трубопровод операций.

  1. пара на каждый элемент в списке с элементом, который следует за ним с помощью zip, поэтому у нас есть список (element, next) пар.
  2. Используйте filter, чтобы сбросить пары, для которых element не передает предикат.
  3. Используйте 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 хорош при работе со списками: при компиляции с оптимизациями, оба подход должен производить примерно тот же машинный код - то есть, нет никакой потери производительности для решения высокого уровня

+0

Вы отказались от лучшей части (IMO) безостановочного решения! 'afterFilter p = map snd. фильтр (стр. fst). (zip <*> хвост) ' – chepner

1

После того, что вы сделали, там некоторые странные вещи с вашим кодом:

map head [ys] очень странно и заставляет останавливать вашу функцию: при первом элементе, соответствующем предикату, ваша функция возвращает список, содержащий его непосредственный преемник, и останавливается там. Вам все равно нужно обработать остальную часть списка.

Также, после определения проблемы, каждый элемент, являющийся преемником элемента, передающего предикат, должен быть на результирующем массиве. Возможно, я ошибаюсь, но я понял, что afterFilter (<0) [-1, -1, 1] должен вернуть [-1, 1].

Однако вы отбрасываете один элемент, который вы не проверяли, позвонив по телефону tail ys: Вы проверили на y, но не на head ys.

Наконец, добавив крайние случаи, вот что вы получите:

afterFilter :: (a -> Bool) -> [a] -> [a] 

afterFilter _ [] = [] 
afterFilter _ [_] = [] 
afterFilter x (y:[email protected](z:zs)) = 
    if x y 
     then z : afterFilter x ys 
    else 
     afterFilter x ys 
0

Try:

afterFilter :: (a -> Bool) -> [a] -> [a] 
afterFilter p [] = [] 
afterFilter p [_] = [] 
afterFilter p (x1:x2:xs) 
    | p x1 = x2:rest 
    | otherwise = rest 
    where rest = afterFilter p (x2:xs) 

Или

afterFilter' :: (a -> Bool) -> [a] -> [a] 
afterFilter' p xs = map snd $ filter (\(x, _) -> p x) $ zip xs (tail xs) 

Или

afterFilter'' :: (a -> Bool) -> [a] -> [a] 
afterFilter'' p xs = [y | (x, y) <- zip xs (tail xs), p x]