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 ?
ToList , the code calls Enumerable. ToList() which is an extension method that return new List<TSource>(source) . In the corresponding constructor, under the worst circumstance, it goes through the item container and add them one by one into a new container. So its behavior affects little on performance.
The ToList<TSource>(IEnumerable<TSource>) method forces immediate query evaluation and returns a List<T> that contains the query results. You can append this method to your query in order to obtain a cached copy of the query results. ToArray has similar behavior but returns an array instead of a List<T>.
ToArray might do an additional allocation and copy operation such that the buffer will be sized exactly to the number of elements. In order to confirm this the following benchmark is used. The results confirm that ToList is in most cases 10% - 15% faster.
Use ToList before you exit the using block that holds your DataContext . Return a query when the caller is likely/obligated to supply additional filtering criteria which will be used by indexes to reduce # of result rows and/or database IO.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With