[Haskell-beginners] Unexpected Space Leak (despite using
external-sort)
Daniel Fischer
daniel.is.fischer at web.de
Mon Jan 4 08:55:47 EST 2010
Am Montag 04 Januar 2010 02:35:04 schrieb Peter Green:
> I am using the external-sort package to sort my output in the program
> below. I made this choice because my output dataset [[Int]] can be
> large (e.g. >3M items, each [Int]).
>
> What my program does:
>
> (1) Reads in a file containing 'compressed lists' which look like so:
>
> 8+12,11,7+13,10
> 1+2+3,1+9,3+6,4
> .
> .
>
> One compressed list per line. These compressed lists are parsed to
> become [[[Int]]]
>
> [[[8,12],[11],[7,13],[10]],
> [[1,2,3],[1,9],[3,6],[4]],
> .
> .
> ]
>
> Generally files of compressed lists have lengths of ~10,000 lines.
>
> (2) Compressed lists are exploded to [[int]] via concatMap Cartesian
> Product over [[[Int]]], so we end up with [[Int]]
>
> [[8, 11, 7, 10],
> [8, 11, 13, 10],
> [12, 11, 7, 10],
> [12, 11, 13, 10],
> [1, 1, 3, 4],
> .
> .
> [3, 9, 6, 4]]
I have a different idea. You want to consume the data in order, don't you?
You can interleave sorting and exploding then:
1. read in data (~10,000 lists of lists. In your examples, each line contains a list of
four short lists, the nested lists containing 1-3 elements. I hope that's not too far from
reality.)
2. map (\l -> ([],l)) to get [([],[[8,12],[11],[7,13],[10]]), ([],[[1,2,3],[1,9],[3,6],
[4]]), ... ]
3. split first sublists of second component and append to the first component (empty list)
concatMap (\(acc,(toSplit:rest)) -> [(acc ++ [x],rest) | x <- toSplit])
[([],[[8,12],[11],[7,13],[10]]),
([],[[1,2,3],[1,9],[3,6],[4]]),
.
.
]
~>
[([8],[[11],[7,13],[10]]),
([12],[[11],[7,13],[10]]),
([1],[[1,9],[3,6],[4]]),
([2],[[1,9],[3,6],[4]]),
([3],[[1,9],[3,6],[4]]),
.
.
]
A list of ~30,000(?) ([Int],[[Int]]) pairs
4. sortBy (comparing fst) {-# import Data.Ord (comparing) #}
5. groupBy ((==) `on` fst) {-# import Data.Function (on) #-}
You now have something like
[
[([1],[[1,9],[3,6],[4]]),
([1],[[?],[?],[?]]),
.
.
],
[([2],[[1,9],[3,6],[4]]),
([2],[[?],[?],[?]]),
.
.
],
.
.
[([131],[[?],[?],[?]])],
([131],[?]),
.
.
]
]
6. If necessary, repeat 3. to 5. on each of the groups to get
[
[
[([1,1],[[3,6],[4]]),
([1,1],[[?],[?]]),
.
.
],
[([1,2],[[?],[?]]),
([1,2],[[?],[?]]),
.
.
],
.
.
]
]
concat to get
[
[([1,1],...),
.
.
],
[([1,2],...),
.
.
],
.
.
]
, iterate until exploding in step 8. produces something manageable.
7. map (\g@((h,_):_) -> (h, map snd g))
You now have something of type [([Int],[[[Int]]])],
[([1],[ [[1,9],[3,6],[4]], [[?],[?],[?]],...]),
([2],[[[1,9],[3,6],[4]],[[?],[?],[?]],...]),
.
.
([131],[[[?],[?],[?]],...])
]
or
[([1,1],[ [[3,6],[4]], [[?],[?]],...]),
([1,2],[ [[?],[?]], [[?],[?]],...]),
.
.
]
8. explode second components in each of the pairs, sort, append to first component,
consume.
Thanks to laziness, the [[[Int]]] of the second group will only be exploded after the
first group has been consumed.
One caveat: if the lines do not always contain lists of equal length, you must not repeat
steps 3.-6. more often than the shortest length or modify the function in 3. to handle
that, e.g.
partialExplode (acc,toSplit:rest) = [(acc + [x],rest) | x <- toSplit]
partialExplode (acc,[]) = [(acc,[])]
> -- Cartesian Product over a List of Lists
> -- Http://www.cs.nott.ac.uk/~gmh/sudoku.lhs
> -- cp [[1,2],[3],[4,5,6]] --> [[1,3,4],[1,3,5],[1,3,6],[2,3,4],[2,3,5],
> [2,3,6]]
> cp :: [[a]] -> [[a]]
> cp [] = [[]]
> cp (xs:xss) = [y:ys | y <- xs, ys <- cp xss]
change that to
cp (xs:xss) = [y:ys | ys <- cp xss, y <- xs]
to get something more memory friendly.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20100104/7aa5bca1/attachment-0001.html
More information about the Beginners
mailing list