[Haskell-cafe] Re: Space usage and CSE in Haskell

ChrisK haskell at list.mightyreason.com
Thu Jul 26 08:03:37 EDT 2007


Melissa O'Neill wrote:
> For example, consider yet another variant of power_list:
> 
> power_list l = [] : pow [[]] l where
>     pow acc []     = []
>     pow acc (x:xs) = acc_x ++ pow (acc ++ acc_x) xs
>        where acc_x = map (++ [x]) acc
> 
> By many standards, this version is inefficient, with plenty of appends 
> and lots of transient space usage.
> 
> BUT, it generates the output in an order that'll accommodate infinite 
> lists, thus we can say:
> 
>    power_list [1..]
> 
> (none of the other versions had this property -- they'd just die here)
> 
> So, the moral for optimizations is that any transformation we do to 
> improve space performance shouldn't make our program stricter than it 
> was before.  (I think the paper by David Sands and Joergen Gustavsson 
> that Janis Voigtlaender mentioned covers this too, but I haven't had a 
> chance to look at it closely yet.)
> 
>     Melissa.
> 
> P.S.   For fun, I'll also note that yes, it *is* possible to code a 
> lazy-list-friendly power_list function in a way that doesn't drag saved 
> lists around, although it doesn't run as nearly as quickly as some of 
> the others seen.
> 
> -- Count in binary and use that to create power set
> power_list xs = loop zero where
>    loop n = case select xs n of
>                 Nothing  -> []
>                 Just set -> set : loop (inc n)
> 
>    select xs     []           = Just []
>    select []     nat          = Nothing
>    select (x:xs) (True:nat')  = select xs nat' >>= \l -> Just (x:l)
>    select (x:xs) (False:nat') = select xs nat'
> 
>    zero = []
>    inc []           = [True]
>    inc (False:bits) = True  : bits
>    inc (True :bits) = False : inc bits
> 
> No doubt this can be coded better yet...

And it can.  Though the speed depends on whether you use and Int or Integer to 
keep track of the length of the input list.  (If you want a power set of a list 
with 2^31 elements then you can change to Integer).

Your code for power_list and mine for powerBin and powerBin2 work in infinite lists:

> *Main> take 10 (power_list [1..])
> [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3],[4],[1,4]]
> *Main> take 10 (powerBin [1..])
> [[],[1],[2],[1,2],[3],[2,3],[1,3],[1,2,3],[4],[3,4]]
> *Main> take 10 (powerBin2 [1..])
> [[],[1],[1,2],[2],[1,2,3],[1,3],[2,3],[3],[1,2,3,4],[1,2,4]]

Though they all disagree about the order involved.  My actual code:

> powerBin [] = [[]]
> powerBin xs = [] : upto (0 :: Int)
>   where upto limit = fromTo limit id (upto (succ limit)) xs
>           where fromTo  n  acc  cont    []  = [] -- reached past end of input list, now done 
>                 fromTo  0  acc  cont (y:_) = (acc . (y:) $ []) : cont
>                 fromTo  n  acc  cont (y:ys) =
>                     let n' = pred n
>                         acc' = acc . (y:)
>                         cont' = fromTo n' acc' cont ys
>                     in fromTo n' acc cont' ys

And a version with acc' and acc switched:

> powerBin2 [] = [[]]
> powerBin2 xs = [] : upto (0 :: Int)
>   where upto limit = fromTo limit id (upto (succ limit)) xs
>           where fromTo  n  acc  cont    []  = [] -- reached past end of input list, now done 
>                 fromTo  0  acc  cont (y:_) = (acc . (y:) $ []) : cont
>                 fromTo  n  acc  cont (y:ys) =
>                     let n' = pred n
>                         acc' = acc . (y:)
>                         cont' = fromTo n' acc cont ys
>                     in fromTo n' acc' cont' ys
> 

The above never uses (++) or 'reverse' but does build a DList of (y:) for 'acc'. 
  If you do not care if the returned lists are individually reversed then you 
can use List for acc with (acc' = (y:acc)).

The performance on ghc-6.6.1 with -O2 on PPC G4 applied to

> main = print (length (power_list [1..22]))

real    0m8.592s
user    0m7.017s
sys     0m0.687s

> main = print (length (powerBin [1..22])) 

real    0m3.245s
user    0m2.768s
sys     0m0.073s

> main = print (length (powerBin2 [1..22])) 

real    0m3.305s
user    0m2.835s
sys     0m0.071s

-- 
Chris Kuklewicz



More information about the Haskell-Cafe mailing list