Stack Builders logo
Arrow icon Insights

Obverse and Reverse

"Obverse, or reverse, I can make nothing out of it." - Mohi

Still other examples will show not only the harmony between obverse and reverse, but how coins were dedicated to more than one divinity.

—Isaac Bassett Choate (Myth in American Coinage)

In section 7.5 of Thinking Functionally with Haskell, Richard Bird uses the reverse function as an example of improving the running time of a function by adding an accumulating parameter to it. He begins with a simple and inefficient definition of reverse, which we'll call obverse:

obverse :: [a] -> [a]
obverse []     = []
obverse (x:xs) = obverse xs ++ [x]

In order to get a better idea of the inefficiency of this function, let's clock it:

> clockSomething (obverse [1..1000000] :: [Integer])
1.20 s
> clockSomething (obverse [1..10000000] :: [Integer])
11.66 s

In search of a more efficient definition of obverse, Bird defines an auxiliary function, reverse', as follows:

reverse' :: [a] -> [a] -> [a]
reverse' xs ys = obverse xs ++ ys

Clearly,

obverse xs == reverse' xs []

An efficient definition of reverse' can be calculated by induction. In the case of an empty list,

reverse' [] ys
  == (by definition of reverse')
obverse [] ++ ys
  == (by definition of obverse)
[] ++ ys
  == (by left identity)
ys

Otherwise,

reverse' (x:xs) ys
  == (by definition of reverse')
obverse (x:xs) ++ ys
  == (by definition of obverse)
(obverse xs ++ [x]) ++ ys
  == (by associativity)
obverse xs ++ [x] ++ ys
  == (by definition of (++) and left identity)
obverse xs ++ (x:ys)
  == (by definition of reverse')
reverse' xs (x:ys)

The efficient definition of reverse' can be used to define an efficient reverse function, as follows:

reverse :: [a] -> [a]
reverse xs = reverse' xs []
  where
    reverse' []      ys = ys
    reverse' (x:xs') ys = reverse' xs' (x:ys)

In order to compare the efficiency of the obverse and reverse functions, let's clock the latter too:

> clockSomething (reverse [1..1000000] :: [Integer])
493.63 ms
> clockSomething (reverse [1..10000000] :: [Integer])
5.08 s

This is not just an example of how to improve a program's performance, but an example of how to use mathematics to find a better algorithm, which is "the best way to improve a program's performance." We're merely using definitions and the fact that lists, the empty list, and the append function form a monoid, but we could do so much more. In short, it's just an example of how to use mathematics as one way to better understand and improve our programs.

(For more information and the definition of clockSomething, see https://github.com/stackbuilders/reverse.)

Published on: Feb. 19, 2015
Last updated: Dec. 20, 2024

Written by:

Software Developer
Juan Pedro Villa

Subscribe to our blog

Join our community and get the latest articles, tips, and insights delivered straight to your inbox. Don’t miss it – subscribe now and be part of the conversation!
We care about your data. Check out our Privacy Policy.