2010-10-05 16 views
5

Supongamos 2 Mapas¿Es malo usar toList?

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)] 

¿Cómo se define una función elegante

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

tal que sparse1 combi sparse2 devuelve 414,0 (= 5 * 19 + 11 * 29), ya 12 y 102 son el único claves comunes de los dos mapas? Hay una función elegante (simple y eficiente) con las listas ya los que serían estrictamente ordenó:

combiList xs ys = cL xs ys 0 
cL [] _ acc = acc 
cL _ [] acc = acc 
cL ([email protected](k,r):xs) ([email protected](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 

Pero es

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

una buena idea saber las listas no son más utilizados en el resto de la código? Y si no, ¿cómo escribirías eficientemente combi sin toList?

Respuesta

7

Usando fold y intersectWith en los mapas es un poco más elegante (y probablemente más rápido):

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

combi sparse1 sparse2 vuelve 414.0 como se desee.

Y si le importa el rendimiento, intente utilizar Data.IntMap: debe ser varias veces más rápido que Data.Map aquí.

+0

Estoy de acuerdo en que es más elegante, pero ¿es más rápido? No creo que en GHC, la generación de un mapa por Map.intersectionWith y el consumo de un mapa por Map.fold estén fusionados, y por lo tanto este código podría ser más lento si hay muchas claves comunes. –

+1

No podemos obtener un rendimiento realmente bueno del 'Data.Map' en este caso. El 'fold' y' intersectionWith' son flojos y darán como resultado la creación de thunks adicionales. – tibbe

Cuestiones relacionadas