1

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.

4 Answers 4

5

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.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks! I knew this question was easy for someone with a little more experience. I did know about anonymous functions but I didn't know about "snd". I tried accessing it with a combination of (!!) but that didn't work. I'll accept your answer as soon as I can :)
@Shadol you don't really need to know about snd either, because pattern-matching is usually preferrable. It definitely is preferrable in this case.
It even worked with my self created filter function :D
@leftaroundabout if by "pattern-matching" you refer to something like my Ackermann Function I created here: ackerF m n | m == 0 = n+1 | m > 0 && n == 0 = ackerF (m-1) 1 | m > 0 && n > 0 = ackerF (m-1) (ackerF m (n-1)) Then I still don't know how to access the element I want with just that knowledge.
@Shadol: I mean, to get at the second element a tuple you can just match the pattern (_,f). Like, by writing that as the function argument, i.e. in this case \(_, f) -> f < 18.5 || f > 25 as shown in the answer.
|
2

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

Comments

1

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.

Comments

0

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 m a 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 -> m b (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)]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.