Even if you fix the typo you'll still get an error, since length
returns an Int
where you need an Integer
. The following is one way to fix this (I've also rewritten your code to use guards):
import Data.List (genericLength)
dreiNplusEins :: Integer -> [Integer]
dreiNplusEins 1 = [1]
dreiNplusEins n
| n `mod` 2 == 0 = n : dreiNplusEins (n `div` 2)
| otherwise = n : dreiNplusEins (n * 3 + 1)
maxZyklus :: UntereGrenze -> ObereGrenze -> (UntereGrenze, ObereGrenze, MaxZyklaenge)
maxZyklus m n
| m == n = (m, n, genericLength $ dreiNplusEins m)
| otherwise = (m, n, 0)
type UntereGrenze = Integer
type ObereGrenze = Integer
type MaxZyklaenge = Integer
You could also use fromIntegral . length
if you don't want the extra import
, but I personally think genericLength
is a little clearer.
Also, if you're interested, here's an arguably nicer way to write the first function:
dreiNplusEins :: Integer -> [Integer]
dreiNplusEins = (++[1]) . takeWhile (/=1) . iterate f
where
f n | even n = n `div` 2
| otherwise = n * 3 + 1
This just says "iteratively apply f
until you hit a 1
, and then tack a 1
on the end".
To find the number in a given range that produces the longest chain, you can use the following function:
longestBetween :: (Enum a, Integral b) => (a -> [b]) -> (a, a) -> (a, b)
longestBetween f (m, n)
= maximumBy (comparing snd)
. zip [m..n] $ map (genericLength . f) [m..n]
The first argument is the function that creates the list and the second is the range. The return value is a tuple containing the desired number in the range and the length of its list. Note that we need these additional imports:
import Data.List (genericLength, maximumBy)
import Data.Ord (comparing)
We can test as follows:
*Main> longestBetween dreiNplusEins (100, 1000)
(871,179)
Implementing the maxZyklus
function you specify in the comments just takes a couple of minor changes at this point:
maxZyklus m n = (m, n, maximum $ map (genericLength . dreiNplusEins) [m..n])
maxZyklus 11 22
gives the desired (11, 22, 21)
.