Efficient heaps in purely functional languages

后端 未结 9 1886
被撕碎了的回忆
被撕碎了的回忆 2021-01-30 13:42

As an exercise in Haskell, I\'m trying to implement heapsort. The heap is usually implemented as an array in imperative languages, but this would be hugely inefficient in purely

9条回答
  •  一向
    一向 (楼主)
    2021-01-30 13:55

    As an exercise in Haskell, I implemented an imperative heapsort with the ST Monad.

    {-# LANGUAGE ScopedTypeVariables #-}
    
    import Control.Monad (forM, forM_)
    import Control.Monad.ST (ST, runST)
    import Data.Array.MArray (newListArray, readArray, writeArray)
    import Data.Array.ST (STArray)
    import Data.STRef (newSTRef, readSTRef, writeSTRef)
    
    heapSort :: forall a. Ord a => [a] -> [a]
    heapSort list = runST $ do
      let n = length list
      heap <- newListArray (1, n) list :: ST s (STArray s Int a)
      heapSizeRef <- newSTRef n
      let
        heapifyDown pos = do
          val <- readArray heap pos
          heapSize <- readSTRef heapSizeRef
          let children = filter (<= heapSize) [pos*2, pos*2+1]      
          childrenVals <- forM children $ \i -> do
            childVal <- readArray heap i
            return (childVal, i)
          let (minChildVal, minChildIdx) = minimum childrenVals
          if null children || val < minChildVal
            then return ()
            else do
              writeArray heap pos minChildVal
              writeArray heap minChildIdx val
              heapifyDown minChildIdx
        lastParent = n `div` 2
      forM_ [lastParent,lastParent-1..1] heapifyDown
      forM [n,n-1..1] $ \i -> do
        top <- readArray heap 1
        val <- readArray heap i
        writeArray heap 1 val
        writeSTRef heapSizeRef (i-1)
        heapifyDown 1
        return top
    

    btw I contest that if it's not purely functional then there is no point in doing so in Haskell. I think my toy implementation is much nicer than what one would achieve in C++ with templates, passing around stuff to the inner functions.

提交回复
热议问题