简体   繁体   中英

Scheme: Printing out first n elements of list

First off, scheme: return a lst that only contains the first element of the lst did not help much, as the question was never really answered, and I followed the contributor's suggestions to no success. Furthermore, I am approaching this with a do loop, and have almost achieved the solution.

I need to make a procedure that will return the first n items in a passed list. For example, (first-n 4 '(5 8 2 9 4 0 8 7)) should give (5 8 2 9).

Here is my approach, the display is there to make sure that the loop is working, which it is:

(define (front-n n list)
  (do ((i 0 (+ i 1)))
    ((> i (- n 1)))
    (display (list-ref list i))))

How do I go about making that return a list, or output a list?

Your do-loop, and @Penguino's recursive function, both fail if there are less than n items in the input list. Here is a simple version based on named-let, renamed take which is the normal name for this function:

(define (take n xs)
  (let loop ((n n) (xs xs) (zs (list)))
    (if (or (zero? n) (null? xs))
        (reverse zs)
        (loop (- n 1) (cdr xs)
              (cons (car xs) zs)))))

Or, if you prefer the recursive function version:

(define (take n xs)
  (if (or (zero? n) (null? xs))
      (list)
      (cons (car xs) (take (- n 1) (cdr xs)))))

The named-let version is preferable to the recursive version, because the recursion isn't in tail position, so it builds a large intermediate stack.

You said that you wanted a version using do . That's harder, because the test that terminates the loop is performed after the action of the loop, and you need to perform the test before the action. You can either test one-ahead, which is awkward, or use this loop that delays the action until after the test has succeeded:

(define (take n xs)
  (let ((zs (list)))
    (do ((n n (- n 1)) (xs xs (cdr xs)))
        ((or (zero? n) (null? xs)) (reverse zs))
      (set! zs (cons (car xs) zs)))))

The set! isn't particularly Schemely, but at least it shares with the named-let version the property that it doesn't build an intermediate stack.

How about

(define (front-n n list)
  (cond ((= 0 n) '())
        (else (cons (car list) (front-n (- n 1) (cdr list))))))

with a little pseudo-error-trapping added. Testing with:

(front-n 4 '(5 8 2 9 4 0 8 7))
(front-n 8 '(5 8 2 9 4 0 8 7))

produces the expected output:

'(5 8 2 9)
'(5 8 2 9 4 0 8 7)
>

Note that the error checking may be useful.

Here is a tail recursive version:

(define (take n a-list)
  (define (iter counter result sublist)
    (cond
      [(empty? sublist) result]
      [(< counter n)
       (iter
         (+ counter 1)
         (append result (list (car sublist)))
         (cdr sublist))]
      [else result]))
  (cond
    [(= n 0) '()]
    [else (iter 0 '() a-list)]))

It differs slightly from the library procedure, because the library procedure throws an error, if you give a take count which is larger than the length of the list, while this function returns the whole list in that case.

Note however, that it makes use of append. I could not figure out a way around that yet.

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