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)的空间开销。