具有所有中间值的列表的总和

时间:2016-11-16 03:18:07

标签: functional-programming scheme racket typed-racket

我正在尝试计算包含所有中间值的列表的总和。我的代码如下,但它不起作用。

(: 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)))))

我自己在家学习球拍,所以我们将非常感谢所有人的帮助!

5 个答案:

答案 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)

我们很聪明,我们看到我们的λ表达式只是kcons的函数组合。我们可以像这样重写它

(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规则(另请参阅),因此它不仅在线性时间内运行,而且在恒定空间中运行,与所有其他变体不同。