Hi Chad,

Scherrer, Chad wrote:
the sum of the values is zero. My specification is

addMaps :: IntMap Int -> IntMap Int -> IntMap Int
addMaps m = IntMap.filter (/= 0) . IntMap.unionWith (+) m

But I'm not really happy with this because it traverses both maps for the union, and then traverses the result to get rid of all the zeros. (This function is a performance bottleneck in my current code).

The function is not so bad as it looks at first sight. First of all, the unionWith and filter are (somewhat) lazy and it is not the case that an entire map will be build before the filter is run. Furthermore, complexity wise, you can not improve the function much -- only a constant factor since the map is traversed twice (assuming that the compiler does no deforestation).

I do not think that we want families of functions like "filterUnion" etc, but maybe it would be good to add an "adjustInsert" function that can insert, delete, or adjust a key in the map -- just what you needed to implement the single-traversal algorithm:

I thought I could do something like

addMaps' :: IntMap Int -> IntMap Int -> IntMap Int
addMaps' = IntMap.foldWithKey f
    where
    f k x = IntMap.update (maybeAdd x) k
    maybeAdd x v = let s = v + x in if s == 0 then Nothing else Just s

However, note that "union" might have a much better runtime behaviour than folding through one map and looking up values in the other map. Through the "hedge" algorithm it more or less takes unions of sub-trees which, depending on the actual runtime structure, generally take much less time. Having said that, one has to be *extremely* careful making any performance remarks about functional data structures -- especially in a lazy setting. I have been surprised many times by benchmark results that changed completely by little changes in how the structure was 'demanded' at runtime. For the same reason, I highly suspect that your original 'bottleneck' in the program is probably not the "filter . union" call, but somewhere else in the program -- maybe even the wrong algorithm? From my experience, it usually doesn't help much to mess with the internal representation of data structures in Haskell. Usually there are so many factors involved that writing a clear algorithm and thinking hard about it works out much better in the long run (together with a handful of seq's and strictness annotations :-) )

I hope this helps,
All the best,
 Daan Leijen


But this is no good, because IntMap.update only affects those keys where lookup succeeds, so
IntMap.update (maybeAdd 3) 8 IntMap.empty
returns IntMap.empty, rather than a map with 8 -> 3 as I had hoped.

What would you suggest to help addMaps run faster? Do I need to pry open the IntMap implementation to do better?

Thanks so much,
Chad Scherrer

PS - I mistakenly crosslisted this to the GHC users list, because I didn't realize how the mailing lists were set up. Please excuse my noobiness.


------------------------------------------------------------------------

_______________________________________________
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell

_______________________________________________
Haskell mailing list
Haskell@haskell.org
http://www.haskell.org/mailman/listinfo/haskell

Reply via email to