I'd like to work sequentially on a list of digits that follow a particular pattern:
99999999999999
99999999999998
...
99999999999991
99999999999989
In other words, 14 digits of decreasing numeric value but only using the digits 1 through 9. Each digit is used individually, so the representation as a number is only for convenience.
For this sort of thing I've always used a combination of a list comprehension and sequence
, like so:
ins = let dig = reverse ['1'..'9'] in sequence (replicate 14 dig)
and relied on Haskell's laziness to ensure not all 14 trillion elements are created at once. Alas, this is back-firing, since my application is exploding in memory usage. I've whittled it waaaaay down to this MRE:
run = let ins = let dig = reverse ['1'..'9'] in sequence (replicate 14 dig)
outs = map (\x -> (x, map digitToInt x)) ins
in filter isGood outs
where isGood (_,i) = if sum i == 50 then trace "Found one." True else False
My expectation is that filter isGood outs
would iteratively evaluate an element in ins
, and discard all those that are not isGood
. What actually happens is that within a few seconds, the application uses GB of RAM even before a single isGood
is found, and it keeps rising rapidly until I kill it. I've tried various combinations of head
and dropWhile
to try to force thunks to be evaluated and unused results to be discarded, but to no effect. I seem to have narrowed the culprit down to the sequence
line.
Can this sort of list comprehension be used without keeping all generated results in RAM? Is there another list generation technique (short of a recursive function that calculates the next element based by parsing the current element) I can use that might do better?
Full compilable program for reference:
module Main where
import Data.Char (digitToInt)
import Debug.Trace (trace)
main :: IO ()
main = do
print run
run = let ins = let dig = reverse ['1'..'9'] in sequence (replicate 14 dig)
outs = map (\x -> (x, map digitToInt x)) ins
in filter isGood outs
where isGood (_,i) = if sum i == 50 then trace "Found one." True else False