2013-03-14 50 views
1

我想通过foldl(或foldl')是最好的方法,当你想产生一个结果列表(即sum)和foldr是最好的方法,当你想生产另一个(甚至可能是无限)列表(即filter)。结合foldl和foldr

所以我正在考虑处理,结合这两个。所以我做了功能sum_fsum_f是相当简单的,它所做的只是将列表中的元素相加,但如果它找到一个元素使得f x为真,则它将当前结果作为列表元素的输出并且从该点开始总结。

的代码是在这里:

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'),即使这些群体呈指数增长。此外,它适用于无限列表(如foldr)。

我想知道我是否可以使用前奏功能而不用显式递归来编写上面的代码(即只有前奏功能中的递归)。或者在这里结合foldlfoldr的想法是否意味着这里的递归不能用标准的前奏功能来完成,并且需要是明确的?

+2

的理解折叠好的资源是文章[“A关于折叠的普遍性和表现性的教程“](http://www.cs.nott.ac.uk/~gmh/fold.pdf)由Graham Hutton提供。 – 2013-03-14 10:53:04

+0

@jug:我意识到foldl不会在恒定的空间中运行,但如果您对严格性谨慎的话,foldl会这样做。我已经编辑了这个问题来用fold取代foldl。 – Clinton 2013-03-14 16:45:49

+1

有点破解:'map sum $ groupBy(\ _ y - > not(power_of_two(y-1)))[1 ..]' – luqui 2013-03-14 18:38:42

回答

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]