tags:

views:

124

answers:

1

Suppose 2 Maps

import qualified Data.Map as M
sparse1, sparse2 :: M.Map Int Float
sparse1 = M.fromList [(1,2.0),(10,3),(12,5),(100,7),(102,11)]
sparse2 = M.fromList [(2,13.0),(11,17),(12,19),(101,23),(102,29)]

How do you define an elegant function

combi :: M.Map Int Float -> M.Map Int Float -> Float

such that combi sparse1 sparse2 returns 414.0 (= 5 * 19 + 11 * 29) because 12 and 102 are the only common keys of the two maps ? There is an elegant (simple and efficient) function with lists since those would be strictly ordered:

combiList xs ys = cL xs ys 0
cL [] _ acc = acc
cL _ [] acc = acc
cL (x@(k,r):xs) (y@(k',r'):ys) acc 
    | k < k'  = cL xs     (y:ys) acc
    | k == k' = cL xs     ys     (acc+r*r')
    | k > k'  = cL (x:xs) ys     acc

But is

combi m1 m2 = combiList (M.toList m1) (M.toList m2)

a good idea knowing the lists are no more used in the rest of the code ? And if not, how would you efficiently write combi without toList ?

+6  A: 

Using fold and intersectWith on the maps is a bit more elegant (and probably faster):

combi :: M.Map Int Float -> M.Map Int Float -> Float
combi x y = M.fold (+) 0 $ M.intersectionWith (*) x y

combi sparse1 sparse2 returns 414.0 as desired.

And if you care about performance, try using Data.IntMap: it should be several times faster than Data.Map here.

Travis Brown
I agree that it is more elegant, but is it faster? I do not think that in GHC, the generation of a map by Map.intersectionWith and the consumption of a map by Map.fold are fused, and therefore this code might be slower if there are many common keys.
Tsuyoshi Ito