[Haskell-beginners] I have created an ugly Haskell program..

Philip Scott haskell-beginners at foo.me.uk
Sun Nov 1 18:27:42 EST 2009

.. and I am positive there must be a way of beautifying it, but I am 
struggling. I bet there is just some lovely way of making this all shrink to 
three lines..

So here's the problem. I have two lists of tuples: (timestamp, value)

What I would like to do in do a kind of 'zip' on two of these lists to make a 
list of (timestamp, (value1, value2)) with the following rules:

- If the timestamps are equal it's easy - make your new element an move on
- If one of the lists has a timestamp that the other doesn't, repeat an old 
value from the other list
- If we don't have an old value yet, then don't create an element in the new 

e.g. if I ran my algorithm on these two lists

d1 = [ (1,"a"), (2,"b"),  (3,"c")           ]
d2 = [          (2,"b'"),          (4,"d'") ]  

I would like to get

result = [ (2, (b,b')), (3, (c,b')), (4, (c,d')) ]  

e.g. there was no data in d2 for our first element so we skipped it.

Okay, so here is my code.. It works, but makes me feel a bit dirty. To explain 
my nomenclature 't' is 'timestamp of', 'v' is 'value of'. vx' and vy' are the 
'old' values from the previous iteration in case a repeat is needed. They are 
Maybes because at the beginning there may be no old value.

d1 = [ (1,"a"), (2,"b"),  (3,"c")           ]
d2 = [          (2,"b'"),          (4,"d'") ]  

t (x,y) = x
v (x,y) = y

js vx' vy' (x:xs) (y:ys)
 | t x == t y  = ( (t x), (v x, v y) )  : js (Just (v x)) (Just (v y)) xs ys
 | t x < t y   =
     maybe (js (Just (v x)) Nothing xs (y:ys))
           (\z -> ( t x, (v x, z ) ) : ( js (Just (v x)) (Just z) xs (y:ys)))
 | t x > t y   =
     maybe (js Nothing  (Just (v y)) (x:xs) ys)
           (\z -> ( t y, (z, v y ) ) : ( js (Just z) (Just (v y)) (x:xs) ys))
js vx' vy' (x:xs) []   = 
    maybe []
          (\z -> ( t x, (v x, z ) ) : ( js (Just (v x)) (Just z) xs []))
js vx' vy' [] (y:ys)   =
    maybe []
          (\z -> ( t y, (z, v y ) ) : ( js (Just z) (Just (v y))  [] ys ))
js _ _ [] []   = []

You call it with the first two arguments as Nothing to kick it off (I have a 
trivial wrapper function to do this)

It works fine:

> :t js
  :: (Ord t) =>
     Maybe a1 -> Maybe a -> [(t, a1)] -> [(t, a)] -> [(t, (a1, a))]

> js Nothing Nothing d1 d2

But it just feels gross. Any advice on how to tame this beast would be greatly 
appreciated :)

All the best, 


More information about the Beginners mailing list