I'm trying to learn Haskell and wondered how to filter a given list, with a function that takes multiple parameters, passing each element of the list with other unchanging elements to the function, to create a new list.
I understand that I can do this to use a bool function to filter the list:
newList = filter theFunction aList
but what happens when the theFunction takes other parameters like this:
theFunction -> elementOfAList -> Int -> Bool
how then could I filter each element of the list, whilst parsing in another element to the function? Any help would be greatly appreciated :)
Edit -> To provide some more information, if I wanted to have a list of integers from [1..10], that get filtered through a function that takes two integers and returns true if the first one is smaller, how could I do that?
In that case you use a partially applied predicate function, like this
-- theFunction :: elementOfAList -> Int -> Bool -- "::" means, "is of type"
newList = filter (flip theFunction i) aList
because
flip theFunction i x = theFunction x i
by the definition of flip
, so flip theFunction
has the type Int -> elementOfAList -> Bool
:
flip :: (a -> b -> c ) -> b -> a -> c
theFunction :: a -> Int -> Bool
flip theFunction :: Int -> a -> Bool
flip theFunction (i :: Int) :: a -> Bool
where i
is some Int
value defined elsewhere. a
is a type variable, i.e. it can be any type, like the type of a list's elements (i.e. for a list aList :: [a]
each element has the same type, a
).
For example, with theFunction x i = x < i
you could call filter (flip theFunction 5) aList
, keeping in the resulting list all the elements of aList
that are smaller than 5. Normally this would just be written as filter (< 5) aList
, with operator sections (of which (< 5)
is one example, absolutely equivalent to the flip theFunction 5
).
The above filtering will use the same Int
value i
in calling theFunction
for every element x
of a list aList
. If you wanted to recalculate that Int
, it is done with another pattern (i.e., higher-order function),
mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
Suppose you wanted to keep in a list of ints all the elements as they are being found by theFunction
. Then you could do it like
theFunction :: elementOfAList -> Int -> Bool
foo :: Int -> [Int] -> [Int]
foo i xs = concat (snd (mapAccumL g i xs)) -- normally written as
-- concat $ snd $ mapAccumL g i xs -- or
-- concat . snd $ mapAccumL g i xs -- or even
-- concat . snd . mapAccumL g i $ xs
where
g acc x -- g :: (acc -> x -> (acc, y)) according to mapAccumL's signature
| theFunction x acc = (x, [x]) -- include `x` in output, and update the acc
| otherwise = (acc, []) -- keep the accumulated value, and skip this `x`
Because both x
and acc
are used in the same role (the first element of the tuple) they both must be of same type.