【问题标题】:Combining foldl and foldr结合 foldl 和 foldr
【发布时间】:2013-03-14 05:50:59
【问题描述】:

我自己发现foldl(或foldl')是您想要将列表汇总为一个结果(即sum)时的最佳方法,而foldr是最好的方法您想生成另一个(甚至可能是无限的)列表(即filter)。

所以我正在考虑将这两者结合起来的处理。所以我做了函数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

例如,让我们将所有按任意 2 的幂分组的正整数相加。这应该输出以下内容:

[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的思想是不是意味着这里的递归不能用标准的前奏函数做,需要显式?

【问题讨论】:

  • Graham Hutton 的文章"A tutorial on the universality and expressiveness of fold" 是了解折叠的好资源。
  • @jug:我意识到 foldl 不会在恒定空间中运行,但是如果您注意严格性, foldl' 可以。我已编辑问题以将 foldl 替换为 fold'。
  • 有点小技巧:map sum $ groupBy (\_ y -> not (power_of_two (y-1))) [1..]

标签: haskell fold


【解决方案1】:

你想要的可以用右折叠来表达,如下所示:

{-# 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]

【讨论】:

    猜你喜欢
    • 2015-01-08
    • 1970-01-01
    • 1970-01-01
    • 2011-08-28
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    相关资源
    最近更新 更多