简体   繁体   中英

Racket: tail recursion on list outputs a reversed list

I want my function to square every number in a given list, also, if an element in lst is a list, the function is applied on it recursively and operates on the elements of that list too.

This is my code:

(define (sqr-up-rec-tail lst)
  (define (helper lst newlist)
    (if (null? lst) newlist
      (if (list? (car lst))
          (helper (cdr lst) (cons (sqr-up-rec-tail (car lst)) newlist))
          (helper (cdr lst) (cons (* (car lst) (car lst)) newlist)))))
  (helper lst ()))

My function works, however, I get a reversed squared list. For example, when I run:

(sqr-up-rec-tail '(2 4 6 (10 20)))

the output I get is:

((400 100) 36 16 4)

I want to get the right output without using the external reverse function. My guess is that I should use append instead of cons, though I didn't get it to work.

Thanks.

append can work, but remember that makes your function O(n²) time since append is basically this:

(define (append a b)
  (if (null? a)
      b
      (cons (car a)
            (append (cdr a) b))))

Since append is such a bad O(n) time and space. It will make your resulting algorithm O(n²) time and space since you use it for each sublist upto the full list of result.

Since you already have a helper you can just use that to make your own reverse :

(define (reverse-helper from to)
  (if (null? from)
      to
      (reverse-helper (cdr from) (cons (car from) to))))

And in stead of returning the result of (helper lst '()) you do (reverse (helper lst '()) '()) .

The problem with using tail recursion here is that the first element that gets processed ends up being the last element in newlist, hence the need for procedures like append and reverse . You can construct the procedure without using append or reverse by avoiding tail recursion, as follows:

(define (map-sqr lst)
  (cond
    [(null? lst) empty]
    [(pair? (car lst))
     (cons (map-sqr (car lst))
           (map-sqr (cdr lst)))]
    [else
     (cons (* (car lst) (car lst))
           (map-sqr (cdr lst)))]))

But, if you must use tail recursion, then as you noted, append can be used while accumulating to get the desired output, as follows:

(define (map-sqr-tail lst)
  (let loop ([lst lst]
             [new-lst '()])
    (cond
      [(null? lst) new-lst]
      [(pair? (car lst))
       (loop (cdr lst)
             (append new-lst (list (loop (car lst) empty))))]
      [else
       (loop (cdr lst)
             (append new-lst (list (* (car lst) (car lst)))))])))

Both implementations work as expected:

> (map-sqr '(2 4 6 (10 20 (30))))
'(4 16 36 (100 400 (900)))
> (map-sqr-tail '(2 4 6 (10 20 (30))))
'(4 16 36 (100 400 (900)))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM