结合foldl和foldr

时间:2013-03-14 05:50:59

标签: haskell fold

我已经发现自己,foldl(或foldl')是最好的方法,当你想要将列表汇总成一个结果(即sum)和{{当你想要产生另一个(甚至是无限的)列表(即foldr)时,1}}是最好的方法。

所以我正在考虑将这两者结合起来的处理。所以我创建了函数filtersum_f非常简单,它只是添加列表的元素,但是如果它找到一个sum_f为真的元素,它会将当前结果作为输出作为列表元素并开始从那一点开始总结。

代码在这里:

f x

现在举例来说,让所有正整数除以任何2的幂。这应输出以下内容:

sum_f :: (Num a) => (a -> Bool) -> [a] -> [a]
sum_f f = 
  let
    sum_f_worker s (x:xs) = 
      let
        rec_call z = sum_f_worker z xs
        next_sum = s + x
      in
        next_sum `seq` if (f x) then next_sum : (rec_call 0) else rec_call next_sum
    sum_f_worker _ [] = []
  in
    sum_f_worker 0

[1, 2, 3+4, 5+6+7+8, 9+10+11+12+13+14+15+16, ...]

我们可以像下面这样做:

[1, 2, 7, 26, 100, ...]

现在这个上面的函数(我相信)在恒定的空间(如import Data.Bits main = let power_of_two x = (x .&. (x - 1)) == 0 -- .&. is bitwise and in print $ take 25 $ sum_f power_of_two [(1::Integer)..] )中运行,即使这些组以指数方式增长。此外,它适用于无限列表(如foldl')。

我想知道是否可以在没有显式递归的情况下使用前奏函数编写上述内容(即只有prelude函数内的递归)。或者将foldrfoldl的想法结合在一起意味着这里的递归不能用标准的前奏函数完成,需要明确吗?

1 个答案:

答案 0 :(得分:5)

您可以使用以下右侧折叠来表达您想要的内容:

{-# LANGUAGE BangPatterns #-}

sum_f :: (Num a) => (a -> Bool) -> [a] -> [a]
sum_f p xs = foldr g (const []) xs 0
  where
    g x f !a = if p x then x+a:f 0 else f (x+a)

Prelude Data.Bits> sum_f (\x -> x .&. pred x == 0) [1..10]
[1,2,7,26]

它适用于无限列表:

Prelude Data.Bits> take 10 . sum_f (\x -> x .&. pred x == 0) $ [1..]
[1,2,7,26,100,392,1552,6176,24640,98432]