我试图在#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
的方式相同,但这不起作用,因为arr
是STArray 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
的使用?
答案 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:True
和False
似乎分布略有错误。我默默地解决了这个问题。