# Quicksort (Erlang)

### From LiteratePrograms

**Other implementations**: AWK | C | C++ | Eiffel |**Erlang**| Forth | Haskell | Java | JavaScript | Mathematica | Mercury | Oz | Python | Python, arrays | Scala | Sed | Standard ML | Visual Basic .NET | XProc

We describe several different implementations of the quicksort algorithm in Erlang. All of these implementations operate on lists. Therefore, they avoid use of the randomized pivot techniques found in, for example, the C implementation of quicksort, since access to a random element of a list takes *O*(*n*) time.

## Contents |

## Implementation

### Using list comprehensions

The most straightforward way to implement quicksort in Erlang is a direct implementation using list comprehensions. It generates two lists, one of elements greater than or equal to the "pivot" element (in this case the first element of the list), and one of elements less than the pivot. These two lists are then recursively sorted, before being concatenated around the pivot to form the final sorted list.

<<qsort1>>=qsort1([]) -> []; qsort1([H | T]) -> qsort1([ X || X <- T, X < H ]) ++ [H] ++ qsort1([ X || X <- T, X >= H ]).

This implementation has the advantage of being very clear and easy to understand, yet quite compact. However, it sacrifices some performance in order to achieve its remarkable clarity.

### Using a partitioning function

One problem with the list comprehension implementation is that it makes two passes through the list to generate the `lesser`

and `greater`

lists. An alternative approach is to partition the list in a single pass. One way to accomplish this is through the standard `lists`

module's `partition`

function, which splits a list based upon some predicate. We have instead elected to implement our own partitioning function. This choice was made partly for pedagogical reasons, but also because our custom partitioning function can accumulate list items that are equal to the pivot, as well as those that are lesser and greater than the pivot. This eliminates the need to process more than once any items that are equal to the pivot.

The new implementation is conceptually the same as the earlier list comprehension implementation, except that the `Lesser`

, `Equal`

, and `Greater`

lists are now generated by the `part`

function.

<<qsort2>>=qsort2([]) -> []; qsort2([H | T]) -> {Less, Equal, Greater} = part(H, T, {[], [H], []}), qsort2(Less) ++ Equal ++ qsort2(Greater). partition function

The partitioning function is a straightforward recursion. The base case has an empty list in its second argument, which corresponds to the list to be partitioned. When the list being partitioned is non-empty, we compare the head of the list to the pivot, and add the head to the appropriate list (lesser, equal, or greater) based on the outcome of these comparisons.

<<partition function>>=part(_, [], {L, E, G}) -> {L, E, G}; part(X, [H | T], {L, E, G}) -> if H < X -> part(X, T, {[H | L], E, G}); H > X -> part(X, T, {L, E, [H | G]}); true -> part(X, T, {L, [H | E], G}) end.

Since the `part`

function performs only a single pass through the list to create the the partitions, this implementation should be faster than the version using list comprehensions.

### Using an accumulator

The `qsort2`

implementation of quicksort is more efficient than the naive implementation, but still involves many costly list traversals to concatenate the sorted list segments together. It also stores lots of sorted list segments as separate lists, which increases the amount of storage required by the implementation. A more efficient alternative is to add an *accumulator* to the quicksort implementation. The accumulator is a list which is used to accumulate (hence the name) sorted list elements. The `qsort3`

implementation initializes the accumulator to the empty list, and calls the auxiliary `qsort3_acc`

function to perform the actual sorting.

<<qsort3>>=qsort3([]) -> []; qsort3([H | T]) -> qsort3_acc([H | T], []). qsort3_acc

The `qsort3_acc`

function is the workhorse of this implementation. This function has two possible cases. If it has run out of unsorted list elements, it returns the accumulated list of sorted elements.

<<qsort3_acc>>=qsort3_acc([], Acc) -> Acc;

If there is a non-empty unsorted list, the first element is pulled off to act as a pivot for partitioning the unsorted list.

<<qsort3_acc>>=qsort3_acc([H | T], Acc) -> part_acc(H, T, {[], [H], []}, Acc). part_acc

The partitioning function resembles the approach to partitioning used for the `qsort2`

implementation. However, unlike the previous partitioning function, when the `qsort3_acc`

partitioning function finishes partitioning the list it recursively calls `qsort3_acc`

on the "greater" portion of the list and the current value of the accumulator. The result is a sorted list of elements greater than the pivot, to which the list of elements equal to the pivot is concatenated. This new list becomes the accumulator for a recursive call of `qsort3_acc`

on the "lesser" portion of the partitioned list.

<<part_acc>>=part_acc(_, [], {L, E, G}, Acc) -> qsort3_acc(L, (E ++ qsort3_acc(G, Acc))); part_acc(X, [H | T], {L, E, G}, Acc) -> if H < X -> part_acc(X, T, {[H | L], E, G}, Acc); H > X -> part_acc(X, T, {L, E, [H | G]}, Acc); true -> part_acc(X, T, {L, [H | E], G}, Acc) end.

The result of using the accumulator is that the list traversals required for segment concatenation in `qsort2`

are eliminated. Instead, the sorted list is assembled as the sorting process takes place. The only concatentation that is required involves the (hopefully short) list of items equal to the pivot.

## Testing

We package the various quicksort implementations into a module called `qsort`

.

<<qsort.erl>>=-module(qsort). -export([qsort1/1, qsort2/1, qsort3/1]). qsort1 qsort2 qsort3

The simple test-driver `test_qsort`

checks that each of the quicksort implementations can successfully sort lists of numbers and of strings.

<<test_qsort.erl>>=-module(test_qsort). -export([start/0]). start() -> io:format("Testing qsort...~n"), test_numeric_sort({qsort, qsort1}), test_string_sort({qsort, qsort1}), test_numeric_sort({qsort, qsort2}), test_string_sort({qsort, qsort2}), test_numeric_sort({qsort, qsort3}), test_string_sort({qsort, qsort3}), halt(). test_numeric_sort({M,F}) -> Stimulus = [3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3], Response = [1,1,2,3,3,3,4,5,5,5,6,7,8,9,9,9], test_sort({M, F}, "numeric sort", Stimulus, Response). test_string_sort({M, F}) -> Stimulus = ["bob","alice","barry","zoe","charlotte","fred","marvin"], Response = ["alice","barry","bob","charlotte","fred","marvin","zoe"], test_sort({M, F}, "string sort", Stimulus, Response). test_sort({M, F}, Testname, S, R) -> FunName = erlang:atom_to_list(F), ModName = erlang:atom_to_list(M), Result = M:F(S), if R == Result -> io:format("~s:~s - passed - ~s.~n", [ModName, FunName, Testname]); true -> io:format("~s:~s - failed - ~s.~n", [ModName, FunName, Testname]) end.

Both of these modules can be compiled at the command line, using the `erlc`

compiler:

$ erlc qsort.erl $ erlc test_qsort.erl

Running `test_qsort`

produces the following results:

$ erl -run test_qsort Erlang (BEAM) emulator version 5.4.12 [source] [hipe] Testing qsort... qsort:qsort1 - passed - numeric sort. qsort:qsort1 - passed - string sort. qsort:qsort2 - passed - numeric sort. qsort:qsort2 - passed - string sort. qsort:qsort3 - passed - numeric sort. qsort:qsort3 - passed - string sort.

Download code |