假设我有一个包含少量关联对(Index, Fruit)
的{{3}}数据:
type Index = Int
data Fruit = Apple | Orange | Tomato
defaultFruit = Tomato
convertFruits :: (Traversable t) => t (Index, Fruit) -> Int -> [Fruit]
convertFruits input n = undefined
convertFruits
应该返回一个填充n
s的长度为Tomato
的列表,但input
包含具有匹配索引的关联对的所有地方除外 - 在这种情况下,来自Fruit
的匹配input
被放置在匹配的索引处。
预期产出的例子:
convertFruits [] 4 = [Tomato, Tomato, Tomato, Tomato]
convertFruits [(2, Apple)] = [Tomato, Apple, Tomato, Tomato]
convertFruits [(1, Orange), (3, Orange), (4, Apple)] = \
[Orange, Tomato, Orange, Apple]
我该如何定义这样的功能?我可以有效地编写纯方法 ,避免使用O(n²)吗?
我发现traverse
采取了Applicative
次行动,而Applicative
看起来很像Monad
。但与良好的“Monad
相反,我实际上并不熟悉Applicative
。有什么帮助吗?
答案 0 :(得分:7)
首先,在这种情况下,您不需要Traversable
,因为您的结果是一个列表。 Foldable
已经足够了。让我们忘记了一秒钟。如果你只坚持列表,convertFruits
会是什么样子?
import qualified Data.Vector as V
import Data.Vector ((//))
-- O(n + length input)
convertFruitsList :: [(Index, Fruit)] -> Int -> [Fruit]
convertFruitsList input n = V.toList $ V.replicate n Tomato // input'
where input' = map (\(ix, f) -> (ix - 1, f)) input
-- Vector is 0-indexed, so we need to adjust the indices
现在,如何为Foldable t (Index, Fruit) -> Int -> [Fruit]
做同样的事情?这也很简单:
import Data.Foldable (toList, Foldable)
convertFruits :: Foldable t => t (Index, Fruit) -> Int -> [Fruit]
convertFruits input n = convertFruitsList (toList input) n
正如您所看到的,根本不需要在此方案中使用traverse
或Applicative
。
答案 1 :(得分:6)
这是ST
monad的完美用法:
import Data.Array.ST
import Data.Array(elems)
import Data.Traversable
type Index = Int
data Fruit = Apple | Orange | Tomato
deriving Show
defaultFruit = Tomato
convertFruits :: (Traversable t) => t (Index, Fruit) -> Int -> [Fruit]
convertFruits input n = elems $ runSTArray $ do
a <- newArray (1,n) defaultFruit
_ <- for input $ uncurry (writeArray a)
return a
ST
允许您使用可变性在纯计算中获得有效的O(n)算法。