我需要某种折叠,可以终止,如果我已经有我想要的数据。
例如,我需要找到前3个大于5的数字。我决定使用Either来终止,我的代码看起来像这样:
terminatingFold :: ([b] -> a -> Either [b] [b]) -> [a] -> [b]
terminatingFold f l = reverse $ either id id $ fold [] l
where fold acc [] = Right acc
fold acc (x:xs) = f acc x >>= flip fold xs
first3NumsGreater5 acc x =
if length acc >= 3
then Left acc
else Right (if x > 5 then (x : acc) else acc)
字符串
有没有更聪明/通用的方法?
3条答案
按热度按时间a1o7rhls1#
函数的结果是一个列表,如果它是惰性生成的,也就是说,从结果中提取一个项只需要评估输入列表,直到在那里找到该项。
Unfolds are under-appreciated。与其关注“消费”输入列表,不如将其视为种子(与内部累加器配对),我们可以逐个元素地从中产生结果。
让我们定义一个
Seed
类型,它包含一个泛型累加器,与输入中尚未使用的部分配对:字符串
现在让我们将
first3NumsGreater5
重新公式化为一个函数,它要么从Seed
中产生下一个输出元素,要么产生没有更多元素的信号:型
现在我们的主函数可以用
unfoldr
来写:型
将其付诸实践:
型
vsikbqxv2#
通常情况下,支持 * 提前终止 * 的文件夹是
foldr
,其第二个参数的组合函数是非严格的。但是,它的信息流是从右到左的(如果有的话),而你希望它是从左到右的。一个可能的解决方案是让
foldr
作为一个 left fold,然后可以让它提前停止:字符串
您需要对
t
进行调整,以测试acc
而不是x
,以满足您的目的。这个函数是
foldlWhile
从https://wiki.haskell.org/Foldl_as_foldr_alternative,重写了一点。foldl'Breaking
从那里可能更适合法案。使用lazy reducer函数的
foldr
可以像unfoldr
一样完美地表达协递归。而且你的代码已经很懒惰了:
terminatingFold (\acc x -> Left acc) [1..]
=>[]
。这就是为什么我不确定这个答案是否像你要求的那样“更聪明”。型
这可以通过抽象测试和计数来进一步推广。
当然,它只是重新实现了
take 3 . filter (> 5)
,但它展示了如何在foldr
中实现它。e1xvtsh33#
如果你使用
foldl
的一元版本foldM
,你所采取的方法就可以工作。使用Either
monad,foldM
将终止于Left
结果,所以我们可以调整你的代码:字符串