如何推广这种就地选择排序?

时间:2016-12-13 21:18:01

标签: haskell

以下是ST Monad中选择排序的实现。输入数组将复制到STUArray s Int Int thaw,然后副本就地排序。

selectionSort :: UArray Int Int -> UArray Int Int
selectionSort arr = runSTUArray $ do
  let (l, n) = bounds arr
  a <- thaw arr
  forM_ [l..n] $ \i -> do
    minIdx <- newSTRef i
    forM_ [i..n] $ \j -> do
      currentMin <- readSTRef minIdx
      jVal <- readArray a j
      minVal <- readArray a currentMin
      when (jVal < minVal) (writeSTRef minIdx j)
    currentMin <- readSTRef minIdx
    iVal <- readArray a i
    minVal <- readArray a currentMin
    writeArray a i minVal
    writeArray a currentMin iVal
  return a

使用FlexibleContexts,我想将类型概括为:

(IArray UArray a, Ord a, Ix i, Enum i) => UArray i a -> UArray i a

但是,这会导致以下类型错误:

Could not deduce (MArray (STUArray s) a (ST s))
  arising from a use of `thaw'
from the context (IArray UArray a, Ord a, Ix i, Enum i)

如何更改selectionSort的约束以允许此概括?

1 个答案:

答案 0 :(得分:5)

array的类API无法正确隐藏s状态参数。当您编写runSTUArray action时,actions类型参数作为输入。在selectionSort的类型注释中,我们必须编写MArray (STUArray s) a (ST s),但这没有意义,因为在运行的操作中使用的s参数在这里甚至不在范围内。在这里提到s只是引入了一个新的不同s参数,因此出现了歧义错误。

constraint包对这类事情有一个很好的解决方案。对于来自Data.Constraint.ForallForall,我们可以表明约束必须适用于类型参数的任意选择。在我们的情况下,我们可以表示MArray (STUArray s) a (ST s)必须适用于s,并且在ST操作中我们可以将量化约束实例化为我们需要的特定s

{-# language UndecidableInstances, ScopedTypeVariables #-}

import Data.STRef
import Control.Monad
import Control.Monad.ST.Strict
import Data.Constraint.Forall
import Data.Constraint
import Data.Proxy

首先,我们必须创建一个可以插入Forall的包装类。

class    (MArray (STUArray s) a (ST s)) => MArray' a s
instance (MArray (STUArray s) a (ST s)) => MArray' a s

现在Forall (MArray' a)成为约束,我们可以从中为MArray' a s生成s约束,而MArray' a s则通过超类MArray (STUArray s) a (ST s)约束(我们实际需要)。

为方便起见,我们需要一个替代的runner函数,它使s输入类型参数更加明确,因此我们可以在正文中引用它:

runSTUArray' :: (forall s. Proxy s -> ST s (STUArray s i e)) -> UArray i e
runSTUArray' f = runSTUArray (f Proxy)

现在可以编写一般selectionSort,我们发现它可以专门用于以前的类型:

selectionSort ::
  forall i a.
  (IArray UArray a, Ord a, Ix i, Enum i, Forall (MArray' a))
  => UArray i a -> UArray i a
selectionSort arr = runSTUArray' $ \(s :: Proxy s) -> do
  let (l, n) = bounds arr

  -- we use "inst" and a type annotation on its result to instantiate
  -- the Forall constraint to the current "s"
  case inst of
    (Sub (Dict :: Dict (MArray' a s))) -> do

      a <- thaw arr
      forM_ [l..n] $ \i -> do
        minIdx <- newSTRef i
        forM_ [i..n] $ \j -> do
          currentMin <- readSTRef minIdx
          jVal <- readArray a j
          minVal <- readArray a currentMin
          when (jVal < minVal) (writeSTRef minIdx j)
        currentMin <- readSTRef minIdx
        iVal <- readArray a i
        minVal <- readArray a currentMin
        writeArray a i minVal
        writeArray a currentMin iVal
      return a

selectionSort' :: UArray Int Int -> UArray Int Int
selectionSort' = selectionSort