问题
Say I have a list of all the integers from 2 to 20.
[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
I also have a function f x that returns either True or False. When I apply this function to the element at position n and it equals to True, I want to remove it and its precedent element (which is the element at position n-1). I want to keep doing this until the list is empty of elements for which the function equals to True, as well as their preceding elements.
Example:
Let's say that the element at position 11, which equals to 13, fits the predicate. I then want to remove the element at position 10, which equals to 12, as well. After that my final list would be:
[2,3,4,5,6,7,8,9,10,11,14,15,16,17,18,19,20]
Let's also say that the elements at position 4, 8 as well as 15 are the only elements (apart from the element at position 13) that fit the predicate. After removing them and their preceding elements my final list would look like:
[2,3,4,7,8,11,14,15,18,19,20]
I'm an inexperienced Haskell programmer, and just playing around for fun. I thought of using some kind of lambda function as predicate to a filter, or creating a function like listRemove xs ys that removes all elements of xs that are also an element of ys, but I feel kind of lost on both.
Any help would be appreciated!
EDIT: what I am trying to do is to solve a Project Euler problem, namely #179. The predicate f x is to check whether x is a prime number or not. Therefore I can surely say that no corner cases exists – e.g. there are no cases such as [x, x, t, t] where t is a number for which the predicate holds, since there exists no two consecutive integers that are both prime except for 2 and 3, which I can easily handle in my solution. Instead, the closest you can get is [x, t, x, t], and in that case I want to remove all of those elements.
回答1:
Solved "Remove elements at positions n and n-1 in a Haskell list, when n fits a predicate"
filter' :: (a -> Bool) -> [a] -> [a]
filter' f xs = map (\i -> xs!!i) $
[i | i <- [0 .. s], fit i && (i >= s || fit (i+1))]
where s = length xs - 1
fit i = not (f (xs!!i))
usage
*Main> filter' (==4) [1,2,3,4,5,6]
[1,2,5,6]
*Main> filter' (\n -> n `mod` 7 == 0) [1..23]
[1,2,3,4,5,8,9,10,11,12,15,16,17,18,19,22,23]
*Main> filter' (\n -> n `elem` [4,5,6]) [1..10]
[1,2,7,8,9,10]
With O(n) cost may be
filter' :: (a -> Bool) -> [a] -> ([a], Bool)
filter' _ [] = ([], False)
filter' f [x] = if f x then ([], True) else ([x], False)
filter' f (y:xs) = case filter' f xs of
(xs', True) -> (xs', f y)
(xs', False) -> if f y then (xs', True) else (y:xs', False)
using standar functions
filter' f xs = filter (not.f) $ map fst $ filter (not.f.snd) $ zip xs $ tail xs ++ [last xs]
回答2:
Assuming you have:
disallowed :: Int -> bool
-- A function that matches your example
disallowed x = elem x [6, 10, 13, 17]
What you want is just
import Data.List (tails)
map head . filter (not . any disallowed . take 2) . filter (not . null) . tails $ [2..20]
If you want to give it a name:
filterWithTails :: ([a] -> Bool) -> [a] -> [a]
filterWithTails f = map head . filter f . filter (not . null) . tails
filterWithTails (not . any disallowed . take 2) [2..20]
(not . any disallowed . take 2) is how you want to filter a list considering the remainder of the list when filtering. It'd be hard to give a better name than the composition of the functions that make it up.
来源:https://stackoverflow.com/questions/21185550/remove-elements-at-positions-n-and-n-1-in-a-haskell-list-when-n-fits-a-predicat