我正在尝试计算包含所有中间值的列表的总和。我的代码如下,但它不起作用。
(: sums : (Listof Integer) -> (Listof Integer))
;; compute the sum of a list,
;; produce all the intermediate sums along the way
;; start with 0
;; (sums (list 1 2 3 4)) ==> (list 0 1 3 6 10)
(define (sums x)
(match x
('() (list 0))
((cons hd '()) (append (list 0) (list (+ 0 hd))))
((cons hd (cons a b))
(append (list 0) (list (+ 0 hd)) (list (+ 0 hd a))) (sums (cons a b)))))
我自己在家学习球拍,所以我们将非常感谢所有人的帮助!
答案 0 :(得分:2)
所以你想写一个这样的函数
(sums (list)) = (list 0) ;; Your implementation has this right
(sums (list x)) = (list 0 x) = (list 0 (+ x 0))
(sums (list y x)) = (list 0 y (+ y x)) = (list 0 (+ y 0) (+ y (+ x 0)))
(sums (list z y x)) = (list 0 z (+ z y) (+ z y x)) = (list 0 (+ z 0) (+ z (+ y 0)) (+ z (+ y (+ x 0))))
依此类推(我在这里使用非常暗示名称,括号和布局,你会明白为什么)。
请注意,所有结果列表都以0
开头,其余的与上一行的结果相同,只是第一个输入项添加到每个后续项目。
换句话说,我们有
(sums (car x items)) = (cons 0 (add-to-each x (sums items)))
首先,您需要实施
(: add-to-each : Integer -> (Listof Integer))
(define (add-to-each x items)
...)
然后在sums
的实现中使用它。要实现add-to-each
,我们需要观察
(add-to-each x ()) = ()
(add-to-each x (cons y1 ())) = (cons (+ x y1) ())
(add-to-each x (cons y2 (cons y1 ())) = (cons (+ x y2) (cons (+ x y1) ()))
等等。
因为你说这是为了学习球拍,所以我会在这里停下来,看看你是否可以从这里弄明白。
答案 1 :(得分:2)
这是一个简单的尾递归解决方案,其成本与列表的大小成线性关系:
(define (sums l)
(define (subsums prefix l)
(if (null? l)
(reverse prefix)
(subsums (cons (+ (car prefix) (car l)) prefix) (cdr l))))
(subsums '(0) l))
(sums '(2 5 3)) ; => (0 2 7 10)
辅助函数subsums
给出了到目前为止的部分和列表以及仍待处理的列表。它在第一个参数上包含它的第一个元素和列表的第一个元素的总和,并在它和列表的其余部分上重复。最后,反向的第一个参数是预期的结果。
答案 2 :(得分:1)
这是另一种使用延续传递方式的解决方案。它还使用尾递归并使用线性迭代过程在恒定时间内运行。它使用lambda作为表示不完整答案的累加器,在前向方向上构建结果列表。通过迭代所有xs
后,我们将累加器应用于最终总和s
- 当然,同时注意也使用empty
终止列表。这个解决方案特别好,因为我们完成后不必改变答案。
(define (sums xs)
(let loop ((s 0) (xs xs) (k identity))
(if (empty? xs)
(k (cons s empty))
(loop (+ s (car xs)) (cdr xs) (λ (rest) (k (cons s rest)))))))
(sums '(1 2 3 4))
; => '(0 1 3 6 10)
我们很聪明,我们看到我们的λ表达式只是k
和cons
的函数组合。我们可以像这样重写它
(define (sums xs)
(let loop ((s 0) (xs xs) (k identity))
(if (empty? xs)
(k (cons s empty))
(loop (+ s (car xs)) (cdr xs) (compose k (curry cons s))))))
(sums '(1 2 3 4))
; => '(0 1 3 6 10)
答案 3 :(得分:0)
以下是另一种选择:
(define (sums l)
(let loop ((l l)
(sl '(0)))
(if (empty? l) (reverse sl)
(loop (rest l)
(cons
(+ (first l)
(first sl))
sl)
))))
测试:
(sums (list 1 2 3 4))
输出:
'(0 1 3 6 10)
答案 4 :(得分:0)
通常,解决更广泛的问题更容易:概括,解决更普遍的问题,然后专注于原始问题。
在伪代码中,
partial-sums (x . xs) = [ 0 x ... ]
= ( 0 . [x ...] )
= ( 0 . partial-sums-from (0 + x) xs )
= partial-sums-from 0 (x . xs)
因此partial-sums-from
可以实现为递归函数。
结果列表也可以在top-down manner(参见this)中迭代构建,通过左侧折叠执行cons
之前递归调用,根据tail-recursion-modulo-cons规则(另请参阅tailrecursion-modulo-cons),因此它不仅在线性时间内运行,而且在恒定空间中运行,与所有其他变体不同。