views:

104

answers:

3

I am new to Haskell, and programming in general. I am trying to define a function which generates the sequence of Collatz numbers from n. I have:

collatz n = (collatz' n) : 1
   where collatz' n = (takeWhile (>1) (collatz'' n))
          where collatz'' n = n : collatz'' (collatz''' n)
                 where collatz''' 1 = 1
                       collatz''' n = if (even n) then (div n 2) else ((3*2)+1)

When I run this in GHCi, I get the error:

No instance for (Num [t])
  arising from the literal `2' at <interactive>:1:7
Possible fix: add an instance declaration for (Num [t])

I don't know what this means. The problem seems to be appending "1" to the list. This problem emerges because

collatz' n = (takeWhile (>0) (collatz'' n))

generates an infinite sequence of "1"s following the correct Collatz sequence; however,

collatz' n = (takeWhile (>1) (collatz'' n))

generates all Collatz numbers from n except "1". What am I doing wrong?

+6  A: 

(:) :: a -> [a] -> [a]
Your first line collatz n = (collatz' n) : 1 forces 1 to become [a].
I guess you wanted something like (collatz' n) ++ [1]
And you have error in if (even n) then (div n 2) else ((3*2)+1) there should be ((3*n)+1 or something like that else you have collatz''' 7 = 7

ony
Thank you: that makes sense, and clarifies (:) for me.
danportin
+5  A: 

ony's answer is correct, but since you're new to Haskell, maybe this is a clearer explanation. The : operator prepends a value to a list, so doing somelist : 7 is invalid since that's trying to append a value to a list. That's why (collatz' n) : 1 doesn't compile, since the type of (collatz' n) is a list of numbers.

Try replacing the : 1 with ++ [1].

Jacob
`(collatz' n) : []` is fine. It will produce something like `[[a]]` (actually original example is compiled, but there is no type bind for first argument `a` that can satisfy class restrictions: `collatz :: (Integral a, Num [[a]]) => a -> [[a]]`). Problem is that any numbers (i.e. `1`) can be compiled only into value of data which satisfies class `Num a`.
ony
Thanks also: I've rewritten whole functions before in order to force (:) to work. You just saved me lots of time!
danportin
A: 

Another way to go at the problem may be for you to use a Data.Sequence structure instead of a list. Sequences allow you to "snoc" a value (put a value on the back of a sequence) as well as the more usual "cons" (put it on the front of the sequence).

Another solution for you may be to use span to make your own "takeUntil" function.

Let me explain: span p xs gives you the same answer as (takeWhile p xs, dropWhile p xs) for whichever p function and xs list you'd use, the same way that splitAt n xs is the same as (take n xs, drop n xs).

Anyway, you can use span to make your own "takeUntil" function:

takeUntil p xs = taken ++ take 1 dropped where
                 (taken, dropped) = span p xs

This is the form that you were looking for, when you used the collatz n = (collatz' n) : 1 form.

I hope this helps.

BMeph