John
John

Reputation: 429

Haskell - List Comprehension - get Input-Elements

I have some problem with list comprehension, if the input is a list.

In these all III excercises it's not allowed to use: map, filter and concat!!!


Part I

Requirements:

A funktion f1 gets a list xs of tripels (a, b, cs) where a and b are of type Int c is of type [Int]

The function should generate a list of pairs (a · b, b + c), for all c in cs AND in the generated list should appear just those pairs in which the 1st element is bigger than the 2nd one - (a · b) > b + c.

Example:

f1  [(10,20,[1,10,100]),  (4,5,[5,15,25])] 

should return the following list:

[(200,21),(200,30),(200,120),(20,10)]

My attempts:

f1 :: Int -> Int -> [Int] -> [(Int, Int)]
f1 a b cs = [(a*b, b+c)| c<-cs, (a*b)>(b+c)]

It works fine, however not for lists as input.

So I tried several ways, but unfortunately not the right one :-(

f1 :: [(Int, Int, [Int])] -> [(Int, Int)]

1st approach:

f1 xs = [((xs !! 0)*(xs !! 1), (xs !! 1)+c)| c<-(xs !! 2), ((xs !! 0)*(xs !! 1))>((xs !! 1)+c)]

2nd approach:

f1 let (a, b, cs) = xs = [(a*b, b+c)| c<-cs, (a*b)>(b+c)]

3rd approach:

f1 (a b cs) = [(a*b, b+c)| c<-cs, (a*b)>(b+c)]

All three don't work!

Solution by dave4420:

f1 :: [(Int, Int, [Int])] -> [(Int, Int)]
f1 xs = [ (a*b, b+c) | (a, b, cs) <- xs, c <- cs, (a*b)>(b+c) ]

Part II

Requirements:

A function g1 gets a list of pairs of same type and generate a plain list out of it.

Example:

g1 [(1,2),(3,4),(5,6)] returns [1,2,3,4,5,6]

My Attempt:

g1 :: [(Int, Int)] -> [Int]
g1 xs = [a,b | (a,b)<-xs]

I get a compiling error for this because a,b in the output of list comprehension does not have the correct syntax.

However I can return a or b or e.g. a+b:

g1 xs = [a | (a,b)<-xs]

or

g1 xs = [a+b | (a,b)<-xs]

Could you please help me out of this too?

Thanks once again

Part III is coming...

Upvotes: 0

Views: 837

Answers (3)

dave4420
dave4420

Reputation: 47052

f1 :: [(Int, Int, [Int])] -> [(Int, Int)]
f1 xs = [ {-TODO-} | (a, b, cs) <- xs, c <- cs, {-TODO-} ]

I have left a couple of {-TODO-}s for you to fill in.


Part II.

If you had to write

g1' :: [[Int]] -> [Int]

how would you do that? Can you then modify g1' into the g1 you desire?

Upvotes: 3

mhwombat
mhwombat

Reputation: 8136

You're 90% of the way there. Remember that in GHCI you use let before a definition, but inside regular source code, you don't. So let's see what you have so far.

λ> let f1 (a,b,cs) = [(a*b, b+c)| c<-cs, (a*b)>(b+c)]

Notice that I added commas in your tuple. Let's find out the type of f1.

λ> :t f1
f1 :: (Num t, Ord t) => (t, t, [t]) -> [(t, t)]

For now, you can ignore the (Num t, Ord t) => part. This is a function that takes a tuple containing an element, another element, and a list of elements. So we can't pass it the entire list of tuples, only a single tuple.

λ> f1 (10,20,[1,10,100])
[(200,21),(200,30),(200,120)]
λ> f1 (4,5,[5,15,25])
[(20,10)]

That's the first part of the result you want.

λ> f1 (4,5,[5,15,25])
[(20,10)]

And that's the second part. So now you need another function that takes input that looks like this:

[(10,20,[1,10,100]),  (4,5,[5,15,25])]

I.e., we want a function that takes a list of tuples. Building on f1, we can write something like this:

λ> let f2 xs = [f1 x | x <- xs]
λ> f2 [(10,20,[1,10,100]),  (4,5,[5,15,25])]
[[(200,21),(200,30),(200,120)],[(20,10)]]

Notice that the result is a list of lists, and we just want a plain list. Since you're not allowed to use concat, take a look at how groovy or Daniel Fisher approached the problem. Two different methods, both good.

Upvotes: 1

Daniel Fischer
Daniel Fischer

Reputation: 183888

You have come close. Using your

f1 :: Int -> Int -> [Int] -> [(Int, Int)]
f1 a b cs = [(a*b, b+c)| c<-cs, (a*b)>(b+c)]

you first need to rewrite that to take a triple (Int, Int, [Int]). That's a trivial change,

f1_a :: (Int, Int, [Int]) -> [(Int, Int)]
f1_a (a,b,cs) = [(a*b, b+c) | c <- cs, a*b > b+c]

Now, you need to apply that function to each element of the input list of triples. That would be map f1_a xs if you could use map, but since you mustn't, write it as a list comprehension

f2 :: [(Int, Int, [Int])] -> [[(Int, Int)]]
f2 xs = [f1_a x | x <- xs]

But that has one level of lists too much, you want a flat list, so you need to concat the list of lists. You are also not supposed to use concat, so you need to write that as a list comprehension.

Now, what does concat do?

For each of the lists in the argument, for each element in that list, put that element in the result, so

concat' xss = [x | xs <- xss, x <- xs]

Now apply that to f2.

Upvotes: 1

Related Questions