How to do Quicksort in Haskell
Overview
Haskell is a functional programming language, a programming paradigm that focuses on changing data through expressions that don’t have side effects. These expressions are often called pure functions.
Let’s see how to implement one of the most popular sorting algorithms, Quicksort in Haskell.
Algorithm
The quicksort function uses the “divide and conquer” technique for sorting:
-
We choose a pivot element from the list and divide the list into two halves, such that elements less than and equal to the pivot are placed on the left side, and elements greater than the pivot are placed on the right.
-
We repeat the above process for the resulting left and right subarrays until we end up with a list of size 1.
-
At this stage, all elements are already sorted, and we combine all the results to return the final sorted list.
Code
Let’s translate the above algorithm into a Haskell program:
-- Return the list which contains elements less than or equal to the input elementsmallerEq :: Int -> [Int] -> [Int]smallerEq = \v -> \list ->case list of[] -> []x:xs | x<=v -> x:smallerEq v xs_:xs -> smallerEq v xs-- Return the list which contains elements greater than the specified elementgreater :: Int -> [Int] -> [Int]greater = \v -> \list ->case list of[] -> []x:xs | x>v -> x:greater v xs_:xs -> greater v xs-- Sort a list using the quicksort algorithmqsort :: [Int] -> [Int]qsort = \list ->case list of[] -> []x:xs ->qsort (smallerEq x xs) ++ x:qsort (greater x xs)
Explanation
smallerEq function
We write the smallerEq function, which returns a list containing elements smaller or equal to the input element.
-
Line 2: The type annotation,
smallerEq :: Int -> [Int] -> [Int], shows that our function takes an integer (pivot element) and a list of integers and returns a list of integers. -
Line3 to 4: We use the function definition, which shows that our function takes a list and element
v, followed by a case expression that implements the recursive formulation of the problem. -
Line 6:
x:xsis a syntax in Haskell that destructures a list into its head element and the remainder list. If the input element is less than or equal to the head element, we return the listx:smallerEq v xs. This places the head element in the final list and combines it with all the results from the recursive call on the remaining list. -
Line 7: The final case statement implements the flip scenario. If the input element is greater than the head element
x, thenxis not part of the final list hence it returns the recursive callsmallerEq v xs, which combines the result from the remaining list.
greater function
The greater function returns a list containing elements greater than the input element. The greater function is written exactly the same as the smallerEq function but with the <= sign replaced with the > sign. The logic otherwise is the same as above.
quicksort function
Let’s put all the pieces together.
-
Line 20: The first case statement implements the base case. If the input list is empty, then there is nothing to sort, and therefore we return an empty list.
-
Line 21: The second case statement implements the general case. In our
quicksortimplementation, we always choose the head element of the list as the pivot. The function callsmallerEq x xsreturns the list containing elements smaller or equal to the elementx. Similarly, the functiongreater x xsreturns the list containing elements smaller or equal to the elementx. We finally call ourqsortfunction on these resulting lists and we assume our function correctly sorts both the lists. Lastly, we place thexelement in between these sorted lists and concatenate the result to return the final list.