【问题标题】:Functional Programming - Implementing Scan (Prefix Sum) using Fold函数式编程 - 使用折叠实现扫描(前缀和)
【发布时间】:2010-02-11 10:29:51
【问题描述】:

我一直在自学函数式编程,目前正在使用折叠编写不同的高阶函数。我坚持实施扫描(也称为前缀和)。我使用折叠的地图实现如下:

(define (map op sequence)
  (fold-right (lambda (x l) (cons (op x) l)) nil sequence))

我的扫描镜头看起来像:

(define (scan sequence)
  (fold-left (lambda (x y) (append x (list (+ y (car (reverse x)))))) (list 0) sequence))

我的观察是“x”是目前的结果数组,“y”是传入列表中的下一个元素。这会产生:

(scan (list 1 4 8 3 7 9)) -> (0 1 5 13 16 23 32)

但这看起来很丑陋,结果列表的反转在 lambda 内部进行。我更愿意不在结果列表上进行全局操作,因为我的下一个尝试是尝试并行化其中的大部分内容(这是一个不同的故事,我正在查看几篇 CUDA 论文)。

有人有更优雅的扫描解决方案吗?

顺便说一句,我对 fold-left 和 fold-right 的实现是:

(define (fold-left op initial sequence)
 (define (iter result rest)
  (if (null? rest)
   result
   (iter (op result (car rest)) (cdr rest))))
 (iter initial sequence))

(define (fold-right op initial sequence)
 (if (null? sequence)
  initial
  (op (car sequence) (fold-right op initial (cdr sequence)))))

【问题讨论】:

    标签: functional-programming scheme


    【解决方案1】:

    恕我直言,扫描可以很好地表达为fold

    Haskell 示例:

    scan func list = reverse $ foldl (\l e -> (func e (head l)) : l) [head list] (tail list)
    

    应该翻译成这样的

    (define scan
      (lambda (func seq)
        (reverse 
          (fold-left 
           (lambda (l e) (cons (func e (car l)) l))
           (list (car seq))
           (cdr seq)))))
    

    【讨论】:

    • 太棒了,这正是我想要的。
    【解决方案2】:

    我不会这样做。 fold 实际上可以根据scan(扫描列表的最后一个元素)来实现。但scanfold 实际上是正交运算。如果您阅读过 CUDA 论文,您会注意到扫描包含两个阶段:第一个阶段产生折叠结果作为副产品。第二阶段仅用于扫描(当然,这仅适用于并行实现;如果完全不依赖scanfold 的顺序实现会更有效)。

    【讨论】:

    • 你说得对,这不是“这样做的方法”,但这是一个有趣的练习,这就是我这样做的原因。我确实阅读了 CUDA 论文,我不会尝试在实际系统中实际使用它。这只是一种好奇心。
    【解决方案3】:

    imho Dario 通过使用反向作弊,因为该练习是关于用折叠而不是反向折叠来表达。当然,这是一种可怕的扫描表达方式,但它是一种将方形钉子塞入圆孔的有趣练习。

    这里是haskell,我不懂lisp

    let scan f list = foldl (\ xs next -> xs++[f (last xs) next]) [0] list
    scan (+) [1, 4, 8, 3, 7, 9]
    [0,1,5,13,16,23,32]
    

    当然,使用与 Dario one 相同的技巧可以摆脱前导 0:

    let scan f list = foldl (\ xs next -> xs++[f (last xs) next]) [head list] (tail list)
    scan (+) [1, 4, 8, 3, 7, 9]
    [1,5,13,16,23,32]
    

    【讨论】:

    • 我的错,我想我应该在评论之前阅读问题,虽然我不会阅读 lisp,看起来提问者已经有了这个解决方案但不喜欢它?
    • 是的,你看,这是一个很好的解决方案,除了 (last xs) 之外,因为它非常不起作用!我开始怀疑这是否可以轻松完成。
    猜你喜欢
    • 1970-01-01
    • 2017-08-14
    • 2018-12-03
    • 2019-10-13
    • 2011-01-18
    • 2013-06-28
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    相关资源
    最近更新 更多