清理可变数组代码

时间:2015-04-15 08:57:20

标签: arrays haskell monads state-monad

我试图在#34;功能算法设计珍珠中解决MINFREE问题"使用可变数组。问题如下:给定n自然数列表,找到列表中未出现的最小自然数。我非常确定以下是一个解决方案:

minFree2 :: [Int] -> Int
minFree2 ns =  (fromMaybe 0 $ elemIndex True seen)
  where
    bound = (1, length ns)
    seen = elems $ runSTArray $ do
      arr <- newSTArray bound False
      mapM_ (\n -> writeArray arr n False) ns
      return arr

do块对我来说非常迫切,我想将它简化为更实用的样式,以便更好地掌握monad。

我可以得到以下内容(仅重写where子句):

 bound = (1, length ns)
 setTrues arr = mapM_ (flip (writeArray arr) False) ns
 seen = elems $ runSTArray $ newSTArray bound False >>= setTrues

但这不起作用,因为它返回()而不是STArray s i e。我想写一些类似的东西:

    setTrues arr = mapM_ (flip (writeArray arr) False) ns >> arr

与我在SML中编写fn arr => map ....; arr的方式相同,但这不起作用,因为arrSTArray s i e而不是ST s (STarray s i e)。我希望能够通过将arr打包回ST来解决这个问题,但我的尝试是:

    setTrues arr = mapM_ (flip (writeArray arr) False) ns >> arr
    seen = elems $ runSTArray $ newSTArray bound False >>= return . setTrues

产生错误:

No instance for (MArray (STArray s) Bool (STArray s Int))
  arising from a use of `setTrues'

我完全不了解。

有没有一种很好的方法来编写这段代码,最大限度地减少do的使用?

2 个答案:

答案 0 :(得分:2)

这应该有效:

setTrues arr = mapM_ (flip (writeArray arr) False) ns >> return arr
seen = elems $ runSTArray $ newSTArray bound False >>= setTrues

诀窍是让setTrues返回数组而不是(),正如您已经尝试过的那样。

答案 1 :(得分:1)

据我所知,ST monad的主要目的之一是为纯功能代码中的命令式编程提供漏洞。所以我猜,使用ST monad并没有很好的解决方案,这看起来并不太重要。但是,要摆脱命令式样式,您可以使用函数

accumArray :: Ix i => (e -> a -> e) -> e -> (i, i) -> [(i, a)] -> Array i e

来自Data.Array。实际上,此函数的实际实现看起来与您的代码有些相似。

总而言之,更具功能性的解决方案可能如下所示:

minFree2 :: [Int] -> Int
minFree2 ns = fromMaybe 0 $ elemIndex False $ elems seen
  where
    bound = (1, length ns)
    seen :: Array Int Bool
    seen = accumArray (||) False bound $ map (,True) ns

PS:TrueFalse似乎分布略有错误。我默默地解决了这个问题。