简体   繁体   中英

Filter for element in a list of lists in Haskell

I built a list of this structure:

[(Interger, Double)]

The List was created by using a zip over a list of Integers and a list of Doubles of exactly the same size.

Now I want to filter the list for Doubles that are either <18.5 or >25. The problem I have is I can't access the Doubles to use them in the filter function.

It's probably easy but I'm a bloody noob in this language. I googled around a lot and read some other threads but I didn't find an answer.

I got:

filter (<18.5) listexpression

So what I'm struggling with is that listexpression. It's easy if it's a list of single values. I could filter before zipping but then I can't connect the data from the filtered list to the other unfiltered List anymore.

Edit: I forgot to mention. It's a worksheet. We were asked to build filter and map functions ourselves and are not allowed to use any additions to the basic Haskell. Meaning no imports are allowed.

You can do something like this:

Prelude> filter (\p -> (snd p) < 18.5 || (snd p) > 25) [(1, 2.3), (1, 20.0)]
[(1,2.3)]

The lambda function passed to filter , namely

(\p -> (snd p) < 18.5 || (snd p) > 25)

says that for every p , the second element of p must be less than 18.5 or over 25.


Alternatively, you could write it like this

Prelude> filter (\(_, f) -> f < 18.5 || f > 25) [(1, 2.3), (1, 20.0)]
[(1,2.3)]

Here the function says that for any pair whose first value doesn't matter and the second one is f , f must be less than 18.5 or over 25.

Glad to see Ami Tavory's answer solved your problem.

But under that answer, you commented:

I tried accessing it with a combination of (!!) but that didn't work.

With the insight of a teaching assistant [:D], I guess you confused list with tuple in Haskell.

zip returns a list of tuple , whereas (!!) take a list as (the first) argument (hence (!!1) take a single list argument), so (!!1) can't be applied to elements of the list returned by zip , which are of type tuple .

Prelude> :t zip
zip :: [a] -> [b] -> [(a, b)]
Prelude> :t (!!)
(!!) :: [a] -> Int -> a
Prelude> :t (!!1)
(!!1) :: [a] -> a

And you've known that fst and snd are applied to tuple .

Prelude> :t fst
fst :: (a, b) -> a
Prelude> :t snd
snd :: (a, b) -> b

A compact version using point free style would be

 filter ((>18.5).snd) listexpression

This uses the function composition operator . , which reads as: First apply the snd function to a tuple from the list to extract the 2nd value, then apply the comparison to 18.5 to this value.

Just for a variety and some additional information which won't bite...

In Haskell the list type is an instance of Monad class . So a list operation like filter can simply be implemented by a monadic bind operator.

*Main> [(1,2.3),(3,21.2),(5,17.1),(4,24.4)] >>= \t -> if snd t < 25 && snd t > 18.5 then [t] else []
[(3,21.2),(4,24.4)]

Monad is all about handling the contained data in a sequential manner. In the list monad the contained data is the value within the list itself. So the bind operator can be very handy to access to contained values (tuples) of the monadic value (the list of tuples) in a sequential manner.

(>>=) :: Monad m => m a -> (a -> m b) -> m b

The type signature of the monadic bind operator states that it takes a monad type value ma as the first argument (the list of tuples here) and a function as the second argument which takes a pure value and returns a monadic value (takes a tuple and returns a tuple in a list or an empty list in this case).

\t -> if snd t < 25 && snd t > 18.5 then [t] else []

It's critical to understand how and why the list items are applied one by one to the provided function. An entire list is one monadic value and the contained values those accessed by the bind operator are passed to the provided a -> mb (take a pure value and return monadic value) type function. So all of the list items those applied to this function become a monadic value ( [t] if condition satisfies or [] if it fails), are then concatenated by the bind operator to form one monadic return value (in this case a list of tuples those satisfy the condition which is implemented in the lambda function).

This monadic operation can also be implemented with the do notation

do
t <- [(1,2.3),(3,21.2),(5,17.1),(4,24.4)]
if snd t < 25 && snd t > 18.5 then return t else []

[(3,21.2),(4,24.4)]

Of course this terribly resembles the list comprehensions which is in fact a syntactical sugar to the monadic list operations. So lets implement it for a final time by using the list comprehensions.

*Main> [t | t <- [(1,2.3),(3,21.2),(5,17.1),(4,24.4)], snd t < 25 && snd t > 18.5]
[(3,21.2),(4,24.4)]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM