问题
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