如何编写我的Applicative状态计算?

时间:2014-08-29 13:19:22

标签: haskell

假设我有一个包含少量关联对(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。有什么帮助吗?

2 个答案:

答案 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

正如您所看到的,根本不需要在此方案中使用traverseApplicative

答案 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)算法。