繁体   English   中英

折叠这是恒定空间和短路

[英]Fold that's both constant-space and short-circuiting

我正在尝试构建一个与Prelude product基本相同的Haskell函数。 但是,与该函数不同,它应该具有以下两个属性:

  1. 它应该在恒定的空间中运行(忽略一些像Integer这样的数字类型的事实)。 例如,我希望myProduct (replicate 100000000 1)最终返回1,这与Prelude的product不同,后者耗尽了我的所有RAM,然后给出*** Exception: stack overflow
  2. 当遇到0时它应该短路。例如,我希望myProduct (0:undefined)返回0,这与Prelude的product不同,后者给出*** Exception: Prelude.undefined

这是我到目前为止所提出的:

myProduct :: (Eq n, Num n) => [n] -> n
myProduct = go 1
  where go acc (x:xs) = if x == 0 then 0 else acc `seq` go (acc * x) xs
        go acc []     = acc

这完全符合我对列表的要求,但我想将其概括为类型(Foldable t, Eq n, Num n) => tn -> n 是否可以使用任何折叠进行此操作? 如果我只使用foldr ,那么它会短路但不会是恒定空间,如果我只使用foldl' ,那么它将是恒定空间但不会短路。

如果你的功能略有不同,那么如何将它变成foldr更为明显。 即:

myProduct :: (Eq n, Num n) => [n] -> n
myProduct = flip go 1 where
    go (x:xs) = if x == 0 then \acc -> 0 else \acc -> acc `seq` go xs (acc * x)
    go [] = \acc -> acc

现在go已经有了foldr味道,我们可以填补这些漏洞。

myProduct :: (Foldable t, Eq n, Num n) => t n -> n
myProduct = flip go 1 where
    go = foldr
        (\x f -> if x == 0 then \acc -> 0 else \acc -> acc `seq` f (acc * x))
        (\acc -> acc)

希望您可以在之前的显式递归样式中看到每个部分的来源以及转换的机械性。 然后我做了一些美学调整:

myProduct :: (Foldable t, Eq n, Num n) => t n -> n
myProduct xs = foldr step id xs 1 where
    step 0 f acc = 0
    step x f acc = f $! acc * x

我们都完成了! 在ghci中进行一些快速测试表明,它仍然根据需要在0上短路并使用恒定空间。

你可能正在寻找foldM 用它实例m = Either b ,你会得到短路行为(或者Maybe ,取决于如果你有很多可能提前退出值,或者一个事先已知)。

foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b

我记得讨论是否应该有foldM' ,但是IIRC GHC大部分时间做的都是正确的。

import Control.Monad
import Data.Maybe

myProduct :: (Foldable t, Eq n, Num n) => t n -> n
myProduct = fromMaybe 0 . foldM go 1
  where go acc x = if x == 0 then Nothing else Just $! acc * x

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM