haskell - Merge two association lists with running accumulator -
basically, describe union/merge of [(,)] combined running accummulator on snd of pair... there elegant way implement this?
(please reference code in context of answering question. if want review code, great too, please on other site: http://codereview.stackexchange.com/questions/54993/merging-time-series)
a time series,
data model variant :: [(day, a)] -> model deriving (show) ... type a in [(day, a)] represents "total balance" e.g. bank account.
some illustration data,
day1 = fromgregorian 1987 10 17 day2 = fromgregorian 1987 10 18 day3 = fromgregorian 1987 10 19 day4 = fromgregorian 1987 10 20 day5 = fromgregorian 1987 10 21 day6 = fromgregorian 1987 10 22 m1 = variant [(day1, 1), (day3, 3), (day5, 5)] :: model integer m2 = variant [(day1, 1), (day2, 2), (day4, 4), (day6, 6)] :: model integer now, merge 2 time series such "total balance" additive,
(&+) :: num => model -> model -> model (variant a) &+ (variant b) = variant $ reverse $ fst $ go b ([],0) go [] [] (xs, c) = (xs, c) go ((da,va):as) [] (xs, c) = go [] (((da,va+c):xs), va+c) go [] ((db,vb):bs) (xs, c) = go [] bs (((db,vb+c):xs), vb+c) go a@((da,va):as) b@((db,vb):bs) (xs, c) | da > db = go bs (((db,vb+c):xs), vb+c) | da < db = go b (((da,va+c):xs), va+c) | da == db = go bs (((da,va+vb+c):xs), va+vb+c) so,
what = m1 &+ m2 variant [(1987-10-17,2),(1987-10-18,4),(1987-10-19,7),(1987-10-20,11),(1987-10-21,16),(1987-10-22,22)]
the moment saw reverse felt there might trouble. here's version lazier , works on infinite values. depend upon each of inputs beingness sorted day, however. first seek merge 2 streams
merge :: num => model -> model -> model merge (variant xs) (variant ys) = variant (go xs ys) go [] ys = ys go xs [] = xs go xx@((dx, vx):xs) yy@((dy, vy):ys) | dx < dy = (dx, vx) : go xs yy | dx > dy = (dy, vy) : go xx ys | otherwise = (dx, vx + vy) : go xs ys it's core of had, much simpler. typically, if can create computation lazy in haskell it's worth effort may more efficient. after point, we'll accumulate
accum :: num => model -> model accum (variant xs) = variant (go xs 0) go [] _ = [] go ((d, v):xs) c = allow c' = v + c in (d, c') : go xs c' and combining these 2 desired result
-- (&+) :: num => model -> model -> model -- &+ b = accum (merge b) though, might improve leave merge , accum exposed api since can combined in many ways other (&+).
it may worth noting obvious way write accum function right fold
accum' :: num => model -> model accum' (variant xs) = variant (snd $ foldr go (0, []) xs) go (d, v) (c, res) = allow c' = v + c in (c', (d, c'):res) doesn't work because accumulates parameter rear of list. trying left fold works have reverse list—a double sin against laziness.
accum'' :: num => model -> model accum'' (variant xs) = variant (reverse $ snd $ foldl go (0, []) xs) go (d, v) (c, res) = allow c' = v + c in (c', (d, c'):res) which gives hint happening in original version. can write right fold, however, have little tricky in order pass accumulator in right direction
accum' :: num => model -> model accum' (variant xs) = variant (foldr go (const []) xs 0) go (d, v) rest c = allow c' = v + c in (d, c') : rest c' note here result of foldr go (const []) xs value of type a -> [a].
haskell
No comments:
Post a Comment