【问题标题】:Lifting foldr to monad将文件夹提升到 monad
【发布时间】:2013-06-11 23:32:19
【问题描述】:

我有函数smallStep :: Command -> Data -> Either StepError Data,我想让bigStep :: [Command] -> Data -> Either StepError Data 使用它,具有以下语义:

bigStep []   data   = Right data
bigStep c:cs data   = do
               data' <- bigStep cs data
               smallStep c data'

这没什么复杂的,但是如果smallStepCommand -&gt; Data -&gt; Data 类型,我将实现bigStep 就像bigStep commands data = foldr data smallStep commands

当然,我也想在这里使用foldr。我该怎么做呢? foldM 被提升 foldl,倒车列表听起来不是一个非常好的主意。

【问题讨论】:

  • 最后一行应该是smallStep c data',不是吗?问题:如果稍后smallStep 失败了怎么办?如果这不能发生,则类型错误。如果它可能失败,并且您不想将该失败转变为模式匹配错误,那么据我所知,您的选择是您所拥有的,或者反转和foldM。倒车实际上可能会更好,因为bigStep (c:cs) d = bigStep cs d &gt;&gt;= smallStep c 可能会产生更大的冲击。
  • 您仍然可以使用foldr。你只需要给它一个合适的 reducer 函数:(a -&gt; b -&gt; b) = (Data -&gt; E Command -&gt; E Command)(其中E = Either StepError)。你有一个(Data -&gt; Command -&gt; E Command)。那么如何将a -&gt; E b 类型的函数扩展E a -&gt; E b 类型的函数?提示:这是 monad 的基本操作之一。
  • @DanielFischer 我不认为我理解你。如果smallStep 不会失败,那么bigStep 将只是一个foldr。但它可以,所以我必须在Either monad 周围跳舞。
  • bigStep cs d = foldr ((=&lt;&lt;) . smallStep) (return d) cs

标签: haskell monads fold


【解决方案1】:

一般来说,就资源使用而言,左折叠不会比右折叠更好或更差。但是,我会假设 [Command] 应该是一个 sequenced 命令列表,这些命令旨在按照提供的顺序一个接一个地执行。如果是这种情况,从一开始就向后构建这些列表可能是最简单的(而不是颠倒它们 - 对于长列表来说,这确实是一项代价高昂的操作)。如果你不想这样做,这里有一个通用的单子右弃牌:

foldrM :: Monad m => (a -> b -> m b) -> b -> [a] -> m b
foldrM f d []     = return d
foldrM f d (x:xs) = (\z -> f x z) =<< foldrM f d xs

注意以下所有类型:

foldl :: (a -> b -> a) -> a -> [b] -> a
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a

foldr :: (a -> b -> b) -> b -> [a] -> b
foldrM :: Monad m => (a -> b -> m b) -> b -> [a] -> m b

我们可以推断foldrM 确实是一个正确的弃牌。

但是,如果您需要fold 一个非常大的列表,那么上面的两个单子折叠都是惰性的,并且只会在最后一个函数应用程序排序后才开始评估。

【讨论】:

    猜你喜欢
    • 2017-01-04
    • 2023-03-29
    • 2018-08-27
    • 1970-01-01
    • 1970-01-01
    • 2019-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多