15

Could somebody provide an in-place quicksort haskell function? I.e. it returns a new sorted list, but the input list is copied to a mutable array or something.

I want to see how to do this, because I have a performance critical program where i need to simulate races and tally scores. If I use immutable data structures for this, each race will take O(log(numRaces) + numRunners) time, whereas if I use mutable arrays etc, each race will take O(log(numRaces)) time.

oh by the way i didn't actually need to do quicksort, i just wanted an example to see how to use mutable arrays effectively

  • Have you seen the vector-algorithms package, http://hackage.haskell.org/package/vector-algorithms? I don't think it has quicksort, but it does have introsort, mergesort, and other useful sorts. If you really need quicksort, an in-place version would be very similar to an imperative implementation. – John L Mar 11 '11 at 02:10
  • 2
    I don't see your reasoning here. How exactly will immutable data structures increase the big-Oh complexity? I'm not aware of any sorting algorithm faster than O(n log(n)) in the average case. – Dan Burton Mar 11 '11 at 05:50
  • 1
    @Dan, indeed, there is provably no such algorithm (as long as you are using only element-element comparison; contrast radix sort) – luqui Mar 11 '11 at 07:02
  • here's [the answer](http://stackoverflow.com/a/7833043/849891) to another question which seems to fit. – Will Ness Mar 03 '12 at 22:42

4 Answers4

30

Here's a version, just to prove you can convert code from Wikipedia almost exactly into Haskell ;)

import Control.Monad.ST
import Data.Array.ST
import Data.Foldable
import Control.Monad

-- wiki-copied code starts here
partition arr left right pivotIndex = do
    pivotValue <- readArray arr pivotIndex
    swap arr pivotIndex right
    storeIndex <- foreachWith [left..right-1] left (\i storeIndex -> do
        val <- readArray arr i
        if (val <= pivotValue)
            then do
                 swap arr i storeIndex
                 return (storeIndex + 1)
            else do
                 return storeIndex )
    swap arr storeIndex right
    return storeIndex

qsort arr left right = when (right > left) $ do
    let pivotIndex = left + ((right-left) `div` 2)
    newPivot <- partition arr left right pivotIndex

    qsort arr left (newPivot - 1)
    qsort arr (newPivot + 1) right

-- wrapper to sort a list as an array
sortList xs = runST $ do
    let lastIndex = length xs - 1
    arr <- newListArray (0,lastIndex) xs :: ST s (STUArray s Int Int)
    qsort arr 0 lastIndex
    newXs <- getElems arr
    return newXs

-- test example
main = print $ sortList [212498,127,5981,2749812,74879,126,4,51,2412]

-- helpers
swap arr left right = do
    leftVal <- readArray arr left
    rightVal <- readArray arr right
    writeArray arr left rightVal
    writeArray arr right leftVal

-- foreachWith takes a list, and a value that can be modified by the function, and
-- it returns the modified value after mapping the function over the list.  
foreachWith xs v f = foldlM (flip f) v xs
porges
  • 30,133
  • 4
  • 83
  • 114
  • 3
    Whoa...it's so...imperative. :) – Dan Burton Mar 11 '11 at 05:45
  • 4
    "you can convert code from Wikipedia almost exactly into Haskell" seems very disingenuous -- It's true only if you have many years of experience with the language, understand how to reason about customized `do` syntax, and understand several deep monad implementations that deal with extremely specialized ways of mutating data or controlling memory layout of data structures. – ely Jan 31 '18 at 19:46
  • 2
    It's not disingenuous, it's true - it's almost a direct translation using the primitive package. And no, it doesn't require years of experience with the language, you really only need to understand monads and ST, neither of which should take years to learn. – chessai May 15 '18 at 15:18
6

See 'sort' in the vector-algorithms package: http://hackage.haskell.org/packages/archive/vector-algorithms/0.4/doc/html/src/Data-Vector-Algorithms-Intro.html#sort

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
2

Check out this code, it has a quick sort version that uses arrays from IO module. You can adapt it to your needs. Keep in mind though that imperative programming in Haskell can give you headaches if you are not careful (mine programs usually suffered from huge memory use and 90% time spent in garbage collection).

Marek Sapota
  • 20,103
  • 3
  • 34
  • 47
-3

syntactically i like this one best:

main :: IO ()
main = do print $ qs "qwertzuiopasdfghjklyxcvbnm"

qs :: Ord a => [a] -> [a]
qs []     = []
qs (x:xs) = qs lt ++ [x] ++ qs gt
                    where
                        lt = [y | y <- xs, y <= x] 
                        gt = [y | y <- xs, y > x]
enihprom
  • 1
  • 1
  • 4
    This is far from in-place. The ST monad stuff in the accepted answer is needed to implement an in-place quicksort in Haskell. – Raman Shah Nov 08 '17 at 02:29