【问题标题】:How do I code my Applicative stateful computation?如何编码我的 Applicative 有状态计算?
【发布时间】:2014-08-29 13:19:22
【问题描述】:

假设我有一个 Traversable 数据,其中包含一些关联对 (Index, Fruit)

type Index = Int
data Fruit = Apple | Orange | Tomato

defaultFruit = Tomato

convertFruits :: (Traversable t) => t (Index, Fruit) -> Int -> [Fruit]
convertFruits input n = undefined

convertFruits 应该返回一个长度为n 的列表,其中填充了Tomatos,除了input 包含具有匹配索引的关联对的所有地方——在这种情况下匹配的Fruit from 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。有什么帮助吗?

【问题讨论】:

  • 只是为了确保我理解 - convertFruits [(2,Apple),(1,Orange),(2,Orange)] 4 的输出应该是 [Tomato,Orange,Orange,Tomato]
  • 能否请你展示一些你想要从这个函数中得到的输入和输出的例子,现在还不是很清楚。
  • @CarstenKönig,未指定/不重要;在我的情况下,有一个运行时保证索引值对将具有唯一索引。所以你的行为很好(任何都是)。
  • @bheklilr,是的,对不起,我的错。稍后我会举例说明。
  • 如果您不以任何方式返回t,则不需要TraversableFoldable 应该足够了。或者你想要t (Index, Fruit) -> Int -> t Fruit?看到你的例子,似乎都错了,你为什么不直接使用Map Index Fruit -> Int -> [Fruit]

标签: haskell


【解决方案1】:

首先,在这种情况下您不需要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

【讨论】:

  • 其实,好主意。然而,我的Traversable 是来自fingertree 包的FingerTree(带有几个自定义幺半群),它没有toList!无赖。
  • @ulidtko:每个Foldable 都有toList。每个Traversable 都是Foldable
【解决方案2】:

这是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) 算法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    • 2021-09-21
    • 1970-01-01
    • 2016-09-24
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    相关资源
    最近更新 更多