I found the following implementation of quicksort in the book titled Learn You Haskell.
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerSorted = quicksort [a | a <- xs, a <= x]
biggerSorted = quicksort [a | a <- xs, a > x]
in smallerSorted ++ [x] ++ biggerSorted
My question is, doesn't this undermine the efficiency of quicksort given that
For an algorithmic point, it is not an in-place algorithm. Hence, it will not be as efficient as the traditional imperative implementations that you know. (Being in-place is a crucial aspect of the superb performance of quicksort-like sorting algorithms.)
It will run with quicksort's typical complexity (on average O(N * log(N))
with worst case O(N²)
). Thus, it is efficient in the sense that it will be able to scale with inputs. But it will not be as fast as highly tuned implementations (e.g. see this question).
As a minor tweak, I remember replacing [x] ++ biggerSorted
by (x:biggerSorted)
was a low-hanging fruit to improve the performance slightly. That was many years ago, so perhaps optimizing compilers today can do that low-level optimization automatically.