Functional programming 包含所有中间值的列表的和

Functional programming 包含所有中间值的列表的和,functional-programming,scheme,racket,typed-racket,tailrecursion-modulo-cons,Functional Programming,Scheme,Racket,Typed Racket,Tailrecursion Modulo Cons,我试图计算一个包含所有中间值的列表的总和。我的代码如下,但它不工作 (: 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)

我试图计算一个包含所有中间值的列表的总和。我的代码如下,但它不工作

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

我在家自学球拍,所以任何帮助都将不胜感激

那么您想编写这样一个函数

(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)
   ...)
然后在实现
和时使用它。要实现添加到每个
,我们需要注意

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


因为你是说你这样做是为了学习球拍,我就到此为止,看看你是否能从这里找到答案。

这是一个简单的尾部递归解决方案,其成本与列表的大小成线性关系:

(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
给出了到目前为止的部分和列表以及仍需处理的列表。它在第一个参数上使用它的第一个元素和列表的第一个元素的和,并在它和列表的其余部分上递归。最后,反向的第一个参数是预期结果。

这里是另一个使用延续传递样式的解决方案。它还使用尾部递归,并使用线性迭代过程以恒定时间运行。它使用lambda作为表示不完整答案的累加器,正向构建结果列表。一旦所有的
xs
都被迭代通过,我们将累加器应用于最终的总和,
s
——当然,同时也要注意用
空的
终止列表。这个解决方案特别好,因为我们不必在完成后反转答案

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

以下是一个备选方案:

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

通常,解决更广泛的问题更容易:概括,解决更一般的问题,然后专门回到原来的问题

在伪代码中

partial-sums (x . xs) = [ 0 x ... ] 
                      = ( 0 . [x ...] )
                      = ( 0 . partial-sums-from (0 + x) xs )
                      = partial-sums-from 0 (x . xs)
因此,来自
的部分和可以实现为递归函数


根据规程(另请参见),在(参见)中,通过在递归调用之前执行
cons
的左折叠,也可以迭代地构建结果列表,这样它不仅可以在线性时间中运行,而且可以在恒定空间中运行,与所有其他变量不同。

使用
而不是
(追加(列表0)
(cons 0…
。最后一行的括号有误。最后一个表达式是
(sums(cons a b))
附加
表达式无效。(顺便说一句
(append(list a)(list b)(list c))
(list a b c)
相同)但是,您的方法可能无法工作,因为您总是从0开始,但递归调用应该从更新的中间和值开始。顺便说一句,这种计算称为部分和(在例如Haskell中抽象为高阶函数
scanl
)。谢谢!这就解决了问题!我不知道每个都应该添加哪些内容。但现在我让它开始工作了!非常感谢!请注意,在实际代码中,您将使用编写
添加到每个
,而不是为了教育目的自己重写它;也就是说,您将编写
(cons 0(map(lambda(y)(+x y))(sums items))
。当然,在这一点上,您可以使用或代替直接递归来编写
求和。这将沿着列表向前构建一个n个嵌套lambda的链,然后应用最后一个lambda,从而使结果列表从末尾开始以正确的顺序构建(因此,向后),就像嵌套的
cons
调用一样。因此它是线性空间和时间(当然不计算结果列表中的n个cons单元格)。但是TRMC解决方案实际上是从顶部向前构建结果列表,即从其头部开始,并使用O(1)的空间开销。