如何使用尾递归将列表加倍?

时间:2018-09-19 22:34:40

标签: racket tail-recursion

(define (lst-double-helper lst acc)
  (if (empty? list)
      acc
      (lst-double-helper (rest lst) (cons (* (first lst) 2) acc))))

(define (lst-double lst)
  (lst-double-helper lst '()))

我觉得我做对了。但这给我一个错误

(lst-double '(1,2,3))
*: contract violation
  expected: number?
  given: ',2
  argument position: 1st
  other arguments...:

为什么期望第二个参数是数字?

4 个答案:

答案 0 :(得分:1)

一些评论:

  • 列表元素由空格而不是逗号分隔。那就是正在报告的错误。
  • 递归的基本情况必须引用参数lst,而不是list
  • 您的尾部递归解决方案会反转列表,最后需要额外的reverse才能恢复原始顺序

进行上述更改后,它可以按预期工作:

(define (lst-double-helper lst acc)
  (if (empty? lst) ; parameter is called `lst`
      acc
      (lst-double-helper (rest lst) (cons (* (first lst) 2) acc))))

(define (lst-double lst)
  (reverse ; required to restore original order
   (lst-double-helper lst '())))

(lst-double '(1 2 3)) ; use spaces to separate elements
=> '(2 4 6) 

请注意,遍历输入列表并cons使用其元素以构建输出列表的尾部递归解决方案将必然颠倒输入列表中元素的顺序。没关系,最后做reverse是正常的。避免在最后反转元素的可能替代方法是在开始时反转输入列表或编写非尾随追溯的解决方案。

答案 1 :(得分:1)

一种这样的方法是通过使用延续传递样式。在这里,我们添加了一个名为return的参数,该参数有效地编码了带有lambda的类似返回的行为。 double现在接受两个参数:要加倍的列表xs和结果的延续return

(define (double xs return)
  (if (empty? xs)
      (return empty)
      (double (cdr xs)
              (lambda (result)
                (return (cons (* 2 (car xs))
                              result))))))

例如,将double应用于'(1 2 3)列表的结果发送到print

(double '(1 2 3) print)
;; '(2 4 6)
;; => #<void>

double的计算结果为最终延续计算的结果;在这种情况下,print的值为#<void>。我们可以使用identity函数来有效地获取价值–

(double '(1 2 3) identity)
;; => '(2 4 6)

使用球拍,您可以轻松地指定默认参数,因此我们可以将double修改为使用identity作为默认延续

(define (double xs (return identity))
  ;; ...
  )

这种样式导致便捷的程序可以同时在两种调用样式下工作:连续传递样式–

(double '(10 11 12) print)
;; '(20 22 24)
;; => #<void>

(double '(10 11 12) length)
;; => 3

(double '(10 11 12) car)
;; => 20

(double '(10 11 12) cdr)
;; => '(22 24)

... 直接样式,使用默认的identity延续

(print (double '(10 11 12)))
;; '(20 22 24)

(length (double '(10 11 12)))
;; => 3

(car (double '(10 11 12)))
;; => 20

(cdr (double '(10 11 12)))
;; => '(22 24)

答案 2 :(得分:0)

使用地图。

(地图(lambda(a)(* a 2))'(1 2 3))

答案 3 :(得分:0)

对于嵌套列表:

(define (atom? x)
  (and (not (null? x))
       (not (pair? x))))

(define (lst-double-helper lst acc)
  (cond ((empty? lst) acc)
        ((atom? (car lst)) (lst-double-helper (rest lst) (cons (* (first lst) 2) acc)))
        (else (lst-double-helper (rest lst) (cons (lst-double (first lst))
                                         acc) ))))

(define (lst-double lst)
  (reverse ; required to restore original order
   (lst-double-helper lst '())))

但是实际上使该函数尾部递归有点没有意义, 因为正如@simmone所述,map会做到

(define (list-doubler lst) 
  (map (lambda (x) (* 2 x)) lst))


(list-doubler '(1 2 3))
;; '(2 4 6)