尾递归中的反转输出

时间:2017-04-14 08:07:37

标签: scheme lisp

(define (sqr-tail lst)
  (define (helper lst res)
    (if (null? lst)
        res
        (cond ((list? (car lst)) 
               (helper (cdr lst) 
                       (cons (helper (car lst) ())
                             result)))
              (else (helper (cdr lst)
                            (cons (expt (car lst) 2) res))))))
  (helper lst ()))

我在方案中有这个尾递归函数sqr列表中的每个元素,但遗憾的是结果与我想要输出的结果相反。

输入:

> (sqr-tail (list 1 2 4 3 (list 1 2 (list 1)) 3 3))

输出:

< (9 9 ((1) 4 1) 9 16 4 1)

感谢。

2 个答案:

答案 0 :(得分:2)

你快到了。

您需要做的只是reverse每个子列表的返回值:

(defun sqr-tail (lst)
  (labels ((helper (lst res)
             (cond ((null lst)
                    (reverse res))
                   ((listp (car lst))
                    (helper (cdr lst) 
                            (cons (helper (car lst) ())
                                  res)))
                   (t (helper (cdr lst)
                              (cons (expt (car lst) 2) res))))))
    (helper lst ())))
(sqr-tail (list 1 2 4 3 (list 1 2 (list 1)) 3 3))
==> (1 4 16 9 (1 4 (1)) 9 9)

或者,在计划中:

(define (sqr-tail lst)
  (define (helper lst res)
    (cond ((null? lst)
           (reverse res))
          ((list? (car lst))
           (helper (cdr lst) 
                   (cons (helper (car lst) ())
                         res)))
          (else (helper (cdr lst)
                        (cons (expt (car lst) 2) res)))))
    (helper lst ()))

答案 1 :(得分:2)

这是Lisp / Scheme列表工作中固有的东西:因为只有真正的结果,构建列表的方式是倒退的。因此,您使用的常见的尾递归循环与累加器方法最终会向后构建结果。对此的简单回答是,在返回结果时需要反转结果,在您的情况下,因为您正在递归(而不是尾部递归)到嵌套列表中,当然也需要反转它们。

这是一个有点清理和错误保护的原始功能版本(注意这是用Racket编写的 - 它可能不是很合法的Scheme,但它很接近):

(define (square-nested-list/reversed l)
  (define (snl-loop lt accum)
    (cond [(null? lt)
           accum]
          [(cons? lt)
           (let ([head (car lt)]
                 [tail (cdr lt)])
             (cond [(list? head)
                    (snl-loop tail (cons (snl-loop head '())
                                         accum))]
                   [(number? head)
                    (snl-loop tail (cons (* head head) accum))]
                   [else (error "mutant horror death")]))]
          [else (error "mutant death horror")]))
  (snl-loop l '()))

因此,为了获得结果,我们需要在返回时反转累加器。这是对上述功能的一个非常小的改动:

(define (square-nested-list/forward l)
  (define (snl-loop lt accum)
    (cond [(null? lt)
           (reverse accum)]
          [(cons? lt)
           (let ([head (car lt)]
                 [tail (cdr lt)])
             (cond [(list? head)
                    (snl-loop tail (cons (snl-loop head '())
                                         accum))]
                   [(number? head)
                    (snl-loop tail (cons (* head head) accum))]
                   [else (error "mutant horror death")]))]
          [else (error "mutant death horror")]))
  (snl-loop l '()))

如果你想要变得非常聪明和纯粹,你现在可以注意到tail-recursive-loop-with-accumulator方法反过来产生结果,所以它的简单例子实际上是reverse

(define (square-nested-list/forward/stupidly-purist l)
  (define (rev l)
    (define (rev-loop lt a)
      (if (null? lt) a (rev-loop (cdr lt) (cons (car lt) a))))
    (rev-loop l '()))
  (define (snl-loop lt accum)
    (cond [(null? lt)
           (rev accum)]
          [(cons? lt)
           (let ([head (car lt)]
                 [tail (cdr lt)])
             (cond [(list? head)
                    (snl-loop tail (cons (snl-loop head '())
                                         accum))]
                   [(number? head)
                    (snl-loop tail (cons (* head head) accum))]
                   [else (error "mutant horror death")]))]
          [else (error "mutant death horror")]))
  (snl-loop l '()))

这样做的人通常只是试图在互联网上获得积分(有更多愚蠢的纯粹方法可以获得更多积分)。

以下是调用这三个函数的结果:

> (define test-data '((1 2 3) (4 5) 6))
> (displayln (square-nested-list/reversed test-data))
(36 (25 16) (9 4 1))
> (displayln (square-nested-list/forward test-data))
((1 4 9) (16 25) 36)
> (displayln (square-nested-list/forward/stupidly-purist test-data))
((1 4 9) (16 25) 36)

其他一些方法

这个问题的一个问题是反转结果&#39;是它涉及走结果以反转它,并且还复制它。曾几何时,这是一个真正的问题,因为机器只有很少的内存而且非常慢。实际上,如果您的列表巨大,它仍然是一个问题。更常见的是,这个问题存在于人们的脑海中,他们和我一样,都记得机器非常慢,只有微小的记忆,或者他们的思想被语言所破坏,鼓励你表现得就像你使用这样的机器(&#39; C程序员知道除了没有价值之外的所有东西的费用)。

旧Lisps提供的这个问题的一个部分答案是一个类似于reverse的函数,但是破坏性地工作:它反转了一个列表,破坏了原始结构。此函数在Common Lisp中称为nreverse。如果它存在于Scheme中,我认为它将被称为reverse!

更完整的答案是首先构建前进列表。你通过欺骗来实现这一点,包括保持对列表最终缺点的引用,并重复用cdr替换为car,而collecting是你正在收集的对象。如果你想在没有你的代码看起来很糟糕的情况下这样做,你需要使用一个宏:我写的那个(对于Common Lisp,而不是Scheme)被称为collecting,因为它收集了前向列表。还有很多其他的。请注意,这种方法需要可变的conses,并且 在现代垃圾收集器的存在下效率不高。

<!-- Button trigger modal --> <button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal"> Launch demo modal </button> <!-- Modal --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <h4 class="modal-title" id="myModalLabel">Modal title</h4> </div> <div class="modal-body"> ... </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-primary">Save changes</button> </div> </div> </div> </div> <form> <div class="radio"> <label> <input type="radio" name="xx" value=true checked>Accept</label> </div> <div class="radio"> <label> <input type="radio" name="xx" value=false>Reject</label> </div> <!-- Drop the attributes that trigger the modal display --> <button type="button" class="btn btn-primary btn-lg conditional-launch">Conditional Modal</button> </form> <script> // Add this in to target the button and bind to the click event $(".conditional-launch").click( function(event) { // Then get the currently selected radio button's value var value = $("input[name=xx]:checked").val(); // Check the value to make sure you want to show the modal if (value === 'true') { $("#myModal").modal('show'); } } ); </script> 这样的宏仍然有他们的位置我认为:不是因为它们使你的代码更快,而是因为它们可以使它更清晰:如果你想将一些结果收集到列表中,那么就这样做,不要做这个奇怪的倒车事。