Scheme: Iterative process to reconstruct a list in original order?

别等时光非礼了梦想. 提交于 2021-02-11 04:55:20

问题


My question is: how to write a procedure that utilises tailcall, and that constructs a list not in the reverse order. To show what I mean, here is an example of a very simple procedure that is iterative, and that creates a copy of a list:

(define (copy-list ls)
    (define (iter cp-ls rest-ls)
      (if (null? rest-ls)
          cp-ls
          (iter (cons (car rest-ls) cp-ls) (cdr rest-ls))))
    (iter '() ls))

The problem is that, due to the iterative order in which the elements are consed together, the returned list ends up reversed. Yes, you could easily solve this by doing (reverse cp-list) instead of just cp-list in the if-block, but the problem with this is that reverse is a recursive process. Utilising this procedure will nullify the tailcall advantage, which is that the stack size grows linearly with the input size.

So, basically, how to write a procedure like the one mentioned that returns the list in correct order without utilising any sort of recursive processes?

Thanks


回答1:


Notice that your iter procedure is almost exactly how reverse is implemented in the first place - no, it's not a recursive process as you mention in the question.

In Racket is simple to check the definition of a procedure: in an edit window without definitions type reverse, right-click it and then select "jump to definition". It'll have a bit of extra code for optimization and error handling, but the core of the algorithm is in the letrec-values part, and it's identical to your iter procedure. So, if we already have that:

(define (reverse ls)
  (let iter ((cp-ls '()) (rest-ls ls))
    (if (null? rest-ls)
        cp-ls
        (iter (cons (car rest-ls) cp-ls) (cdr rest-ls)))))

Then, copy-list is just:

(define (copy-list ls)
  (reverse (reverse ls)))

That's not terribly useful, by the way - if you're not mutating the list, there's no point in copying it. And the reverse of a reverse is just the original thing, isn't it? In fact any implementation of copy-list that operates on immutable lists, for all intents and purposes is equivalent to the identity procedure:

(define (copy-list ls) ls)



回答2:


You can use continuation passing style, return, below -

(define (copy-list ls)
  (let loop ((ls ls) (return identity))
    (if (null? ls)
        (return null)
        (loop (cdr ls)
              (lambda (r) (return (cons (car ls) r)))))))

(copy-list '(1 2 3 4))
; '(1 2 3 4)

Here's what the process looks like -

(copy-list '(1 2 3 4))
(loop '(1 2 3 4) identity)
(loop '(2 3 4) (lambda (r) (identity (cons 1 r))))
(loop '(3 4) (lambda (r) ((lambda (r) (identity (cons 1 r))) (cons 2 r))))
(loop '(4) (lambda (r) ((lambda (r) ((lambda (r) (identity (cons 1 r))) (cons 2 r))) (cons 3 r))))
(loop '() (lambda (r) ((lambda (r) ((lambda (r) ((lambda (r) (identity (cons 1 r))) (cons 2 r))) (cons 3 r))) (cons 4 r))))
((lambda (r) ((lambda (r) ((lambda (r) ((lambda (r) (identity (cons 1 r))) (cons 2 r))) (cons 3 r))) (cons 4 r))) null)
((lambda (r) ((lambda (r) ((lambda (r) (identity (cons 1 r))) (cons 2 r))) (cons 3 r))) '(4))
((lambda (r) ((lambda (r) (identity (cons 1 r))) (cons 2 r))) '(3 4))
((lambda (r) (identity (cons 1 r))) '(2 3 4))
(identity '(1 2 3 4))
'(1 2 3 4)



回答3:


(map (lambda(x) x) l)

will make a copy of the list l and it's not recursivelly written.

(let ((l '(1 2 3 4)))
  ((fold-right (lambda (e acc)
                 (lambda (x) (cons x (acc e))))
               (lambda (x) (list x))
               (cdr l))
   (car l)))

is another form to copy a list without recursion, but using a monoid.

other:

(let ((l '(1 2 3 4)))
  (car ((fold-right (lambda (e acc)
                      (lambda (x) (acc (append x (list e)))))
                    (lambda (x) (list x))
                    (cdr l))
        (list (car l)))))

other:

(let ((l '(1 2 3 4)))
  (cdr ((fold-left (lambda (acc e)
                     (lambda (x) (cons x (acc e))))
                   (lambda (x) (list x))
                   l)
        'first)))

other (suggested by Will):

(let ((l '(1 2 3 4)))
  ((fold-right (lambda (e acc)
                 (lambda (k) (k (acc (lambda (es) (cons e es))))))
               (lambda (z) (z (list))) l)
   (lambda (es) es)))

There are lots of other ways to copy a list. In general, to make a copy, you need to call, either directly or indirectly, cons.

As mentioned in the comments, not all these ways use an iterative process.



来源:https://stackoverflow.com/questions/63827368/scheme-iterative-process-to-reconstruct-a-list-in-original-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!