我不在乎以“功能性”的方式来做。但我确实需要它处于线性时间(而不是 O(n log n)),而且我真的更喜欢类型签名保持不变(即,不添加额外的类型约束)。这是我到目前为止所拥有的,但我不断收到堆栈溢出:
import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.STRef
import System.Random
randomPermute :: RandomGen g => [a] -> g -> ([a],g)
randomPermute l rgen = runST $ newListArray (1,n) l >>= body rgen where
n = length l
body :: RandomGen g => g -> STArray s Int e -> ST s ([e],g)
body rgen arr = do
rgenRef <- newSTRef rgen
let pick i j = do vi <- readArray arr i
vj <- readArray arr j
writeArray arr j vi
return vj
rand lo hi = do rgen <- readSTRef rgenRef
let (v,rgen') = randomR (lo,hi) rgen
writeSTRef rgenRef rgen'
return v
rv <- forM [1..n] $ \i -> do
j <- rand i n
pick i j
rgen <- readSTRef rgenRef
return (rv,rgen)
ascCount x = sum $ map oneIfBig $ zip x $ tail x where
oneIfBig (x,y) = if x<y then 0 else 1
main = do
-- Using String types just for testing
res <- getStdRandom $ randomPermute $ map show [1..1000000]
putStrLn $ show $ ascCount res
现在我对命令式语言的处理告诉我应该有一种方法可以避免一起使用堆栈。但是在 Haskell 中,我似乎无法弄清楚如何。如果我使用未装箱的数组,我发现了一些可行的方法。但就像我说的,我不希望添加额外的约束。有任何想法吗?
编辑:如果有人可以向我解释上面的代码如何消耗堆栈空间,以及为什么我不能简单地避免使用尾递归调用,我也会很感激。我尝试在某些地方使用热切评估,但没有帮助