我一直在自学函数编程,我现在正在使用折叠编写不同的高阶函数。我坚持实施扫描(也称为前缀和)。我使用折叠的地图实现看起来像:
(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论文)。
有没有人有更优雅的扫描解决方案?
BTW我对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)))))
答案 0 :(得分:3)
我不会这样做。 fold
实际上可以用scan
(扫描列表的最后一个元素)来实现。但scan
和fold
实际上是正交操作。如果您已阅读CUDA论文,您会发现扫描包含两个阶段:第一阶段产生折叠结果作为副产品。第二阶段仅用于扫描(当然,这仅适用于并行实现;如果fold
完全不依赖scan
,则{{1}}的顺序实现会更有效。
答案 1 :(得分:3)
Imho扫描在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 :(得分:1)
这是在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相同的技巧可以摆脱领先的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]