0

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...

John
  • 429
  • 3
  • 8
  • 20

3 Answers3

3
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?

dave4420
  • 46,404
  • 6
  • 118
  • 152
  • nice answer, it helped me understand better. – גלעד ברקן May 14 '13 at 18:31
  • I'm not sure if you all get informed by editing of my question, so I wanted to mention, that my question has three parts... thanks – John May 14 '13 at 20:27
  • @John If your question had three parts you should have posted them all at once. The proper etiquette on this site is to accept the correct answer, then ask your new question in a new post. – dave4420 May 14 '13 at 21:17
  • ok, sorry about that. Because these three questions are similar, I thought if I get the 1st one then I'll be able to solve the other two without help by myself. Ok, I'll post the 3rd part in a new post, if I can't get it. Sorry once again. – John May 14 '13 at 21:33
  • could you please tell me the name of such function like `g1'`, so that I can read about it? I never saw this kind of function before. – John May 14 '13 at 21:35
  • @John [Go to Hoogle](http://www.haskell.org/hoogle/), type [`[[Int]] -> [Int]`](http://www.haskell.org/hoogle/?hoogle=[[Int]]%20-%3E%20[Int]) into the search box, press search, and the first result that pops up is... – dave4420 May 15 '13 at 06:11
  • @John But you can modify `f1` into `g1'`. `f1` processes a list inside a tuple inside a list; `g1'` processes a list inside a list. – dave4420 May 15 '13 at 06:27
1

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.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
1

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.

mhwombat
  • 8,026
  • 28
  • 53
  • @Daniel, mhwombat thank to you both for your explainaitons. I'l test them as soon as I finished my other excercises for tomorrow and give you feedback. For now I'l use the solution of dave4420. – John May 14 '13 at 20:06