2

again another question generated by my attempts at the Project Euler questions (follow on from a previous question). I'm having trouble understanding the following line:

print (maximum (map (product . take 13) (tails number)))

Specifically

map (product . take 13) (tails number)

The type signature for the first argument of map from ghci is [c] -> c:

ghci> :t (product . take 13)
(product . take 13) :: Num c => [c] -> c

The type signature for map (product . take 13) from ghci is [[b]] -> [b]:

ghci> :t map (product . take 13)
map (product . take 13) :: Num b => [[b]] -> [b]

Am I right in saying that as the first argument of map should be a function, [[b]] is not referring to a list of lists, but rather to a list of (partially applied) functions generated by (product . take 13), with the second argument for the partial functions coming from (tails number)?

duplode
  • 33,731
  • 7
  • 79
  • 150
Dave0504
  • 1,057
  • 11
  • 30
  • no - `product . take 13` is already a function (here from a list of some `c` (where `c` is an instance of `Num` because we want to multiply `c`s) to `c`) you should read it from right-to-left: first you take all *final-segments* list of numbers (see [`tails`](http://hackage.haskell.org/package/base-4.7.0.1/docs/Data-List.html#v:tails)), then you multiply the numbers (but only the first 13) in there for each of these segments (`map (product . take 13)`), then you take the maximum of these products (remember one for each segment) and finally you print it – Random Dev Aug 17 '14 at 13:23
  • when some code is hard to understand, simplify! `Prelude Data.List> map (take 4) (tails [1..5])` ==> `[[1,2,3,4],[2,3,4,5],[3,4,5],[4,5],[5],[]]`. This is faulty (only 2 subsequences, of length 4, should be considered here); check out my recent answer to your previous question. – Will Ness Aug 17 '14 at 13:28

2 Answers2

3

Here is a point-free version:

euler8 :: (Ord c, Num c) => [c] -> c
euler8 = maximum . map (product . take 13) . tails

let's make this a bit more obvious:

euler8' numbers = 
  let segments = tails numbers
      groups   = map (take 13) segments -- only take up to 13 numbers from each segment
      products = map product groups
      max      = maximum products
  in max

so as you can see - it first gets the final-segments of the numbers-list (this is a list again)

then it uses map to get the product for each of these segments (again a list)

and finaly it searches for the maximum of those products and returns it

PS: I striped the print in both versions - I think the IO will just complicate matters and it's not really important ... you can always print it out afterwards ;)

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • we can take it further apart, as `map (f . g) == map f . map g`. – Will Ness Aug 17 '14 at 13:31
  • yes that is true - but I think the `take 13` part is just a precaution against long runtimes or something (have to admit I did not google the problem itself) and IMO we could just stip it to simplify understanding too – Random Dev Aug 17 '14 at 13:32
  • but ok - let's do it for now – Random Dev Aug 17 '14 at 13:33
  • 1
    and now it is plain, "only take up to 13 numbers" is wrong for the job: the problem talks of subsequences of length 13, not "up to 13". it's not your error, of course. :) – Will Ness Aug 17 '14 at 13:36
  • I know I was a bit quick in my judgment there ... sadyl my edit and your comments are concurrent ;) – Random Dev Aug 17 '14 at 13:36
  • you weren't wrong at all; the code the OP refers to is wrong. :) – Will Ness Aug 17 '14 at 13:37
  • Hm the `take 13` is wrong in principle but will work for the project example because the short tails won't give a maximum, especially as the last digit is zero. It would however break for 11111111111110999999999999, for example. – Ørjan Johansen Aug 17 '14 at 15:35
  • yeah - but the question was more to the types not so much the euler-question so I choose to let it as it is ... but thanks – Random Dev Aug 17 '14 at 15:57
  • 1
    @WillNess Oh hah, I hadn't seen your answer, so I came up with essentially the same example independently. – Ørjan Johansen Aug 17 '14 at 21:43
1

Am I right in saying that as the first argument of map should be a function ?

Yes, the first argument of map should be a function. See it's type:

λ> :t map
map :: (a -> b) -> [a] -> [b]

It takes a function of type (a -> b). But in your case the a -> b refers to (product . take 13):

λ> :t (product . take 13)
(product . take 13) :: Num a => [a] -> a

So, this is a function which takes a list of elements [a] and produces a single value of type a from it. You can actually test this in ghci:

λ> (product . take 14) [1,2,3]
6

For simplicity, it gets applied like this:

(\x -> product (take 14 x)) [1,2,3]

the second argument for the partial functions coming from (tails number)?

tails is a normal function with the type:

λ> :t tails
tails :: [a] -> [[a]]

So, this function accepts a list of element and gives you a list of list of elements. You can play with this around in ghci:

λ> tails [1,2]
[[1,2],[2],[]]

I hope that makes it clear.

Sibi
  • 47,472
  • 16
  • 95
  • 163