Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify the parameter of a function only once

Tags:

haskell

I started with Haskell a few days ago. I have the following piece of code (recursive function):

totaltime t [x] = 0
totaltime t route = tbd + totaltime (t+tbd) (tail(route))
    where tbd = timebetweendepartures (fst(route!!0)) (fst(route!!1)) t (snd(route!!1))

"route" is a list of tuples, e.g: [(3,1), (1,2), (5,2), (2,1)]

What I'm trying to do, is to add (0,0) to the route before the function even starts doing what it's supposed to do.

So if the parameter route is passed as [(3,1), (1,2), (5,2), (2,1)] then the function should receive it as [(0,0), (3,1), (1,2), (5,2), (2,1)]

I could just do totaltime t ((0,0):route), but since the function is recursive this won't work as intended.

Ideas are much appreciated!

like image 968
Adyy Pop Avatar asked Dec 19 '22 03:12

Adyy Pop


2 Answers

Short answer: you can scope the function totaltime into a where clause, and expose another function that prepends the list with (0,0):

totaltime t l = tt t ((0,0):l)
where tt t [x] = 0
      tt t route = tbd + tt (t+tbd) (tail(route))
          where tbd = timebetweendepartures (fst(route!!0)) (fst(route!!1)) t (snd(route!!1))

But that being said, the code is quite inelegant and probably unsafe:

  • you do not define a case for an empty list;
  • the case that is used for the empty list is the second clause, where you call !!0 and !!1 which are functions that are not total.

Furthermore it is not very elegant you call fst and snd. We can introduce patterns in the head of the clause that ususally make it more elegant and easier to understand. We can thus rewrite our tt function to:

tt t [] = ... # create a case for the empty list
tt t [x] = 0
tt t ((x0,_):xys@((x1,y1):_)) = tbd + tt (t+tbd) xys
    where tbd = timebetweendepartures x0 x1 t y1

so now we can use it like:

totaltime t l = tt t ((0,0):l)
    where tt t [] = ... # create a case for the empty list
          tt t [x] = 0
          tt t ((x0,_):xys@((x1,y1):_)) = tbd + tt (t+tbd) xys
              where tbd = timebetweendepartures x0 x1 t y1

Of course in this case, it would be impossible to ever handle the empty list, since totaltime prepends (0,0) we have guarantees that the initial call is a call with at least one element, and we perform recursion on the direct tail, so that means that the list decreases with one at a time.

We still can however improve the function by using three arugments: the t variable; the head of the list, the tail of the list, and rewrite it to:

totaltime t = tt t (0,0)
    where tt _ _ [] = 0
          tt t (x0,_) (xyh@(x1,y1):xyt) = tbd + tt (t+tbd) xyh xyt
              where tbd = timebetweendepartures x0 x1 t y1

Now the code is syntactically total, and furthermore it can boost efficiency (since we avoid unpacking some parts of the list twice.

We can still improve the code. Notice that we only take the first element of the head of the list (we do not use an y0 variable). So we can omit packing and unpackand and rewrite it to:

totaltime t = tt t 0
    where tt _ _ [] = 0
          tt t x0 ((x1,y1):xyt) = tbd + tt (t+tbd) x1 xyt
              where tbd = timebetweendepartures x0 x1 t y1

Now we save on unpacking tuples and it is more clear what we do.

like image 165
Willem Van Onsem Avatar answered Jan 04 '23 16:01

Willem Van Onsem


You could wrap the function into an "entry-point" function that does the pre-processing of the externally received parameters:

 totaltime t route =
   totaltime' t ((0,0):route)
   where
      totaltime' t [x] = 0
      totaltime' t route = .....
like image 37
Thilo Avatar answered Jan 04 '23 18:01

Thilo