简体   繁体   中英

How do I remove the first and last element in a list in Racket using recursion

Essentially I am trying to remove the first and last element in a list. I have currently been able to determine how to remove the last element in the list but i'm struggling how to remove the first element in the list with the last element in the list.

Here is the code I have so far. Is there a way I can modify my code so I am able to include removing the first element in the list.

(define (rid L)
  (cond
    [(empty? L) '()]
    [(empty? (rest L)) '()]
    [(cons (first L) (rid (rest L)))]))

Here is the results I am expecting with my code

(check-expect (rid (list 1 2 3 4 5)) (list 2 3 4))
(check-expect (rid (list "cat" "dog" "giraffe")) (list "dog"))

With many recursive algorithms, it is not uncommon to actually implement them with two procedures: one to set up the initial conditions and a second one to do the actual recursion, like so:

(define (rid-inner li)
  (cond
    [(empty? li) '()]
    [(empty? (rest li)) '()]
    [(cons (first li) (rid-inner (rest li)))]))

(define (rid1 L)
  (define r (if (empty? L) '() (rest L)))
  (rid-inner r))

With (define r (if (empty? L) '() (rest L))) we strip off the first element of the list; no recursion is actually necessary for this step. Then we define the same procedure you had before with a different name and call it with our new list that already has the first element stripped off. If you want the first element stripped off, just strip off the first element; don't overthink it :) .

In a language like Racket that allows closures and nested procedures, you don't actually even need to define both procedures at the top "global" module scope; just define your recursive procedure inside your initial procedure and call it from there. Example:

(define (rid2 L)
  (define r (if (empty? L) '() (rest L)))
  (define (rid-inner li)
    (cond
      [(empty? li) '()]
      [(empty? (rest li)) '()]
      [(cons (first li) (rid-inner (rest li)))]))
  (rid-inner r))

Another, somewhat cleaner, way to do the above is to use a named let , which allows us to simultaneously set up our initial conditions, create a named procedure, and then call that procedure immediately from within itself. We do that like so:

(define (rid3 L)
  (let rid-inner ([li (if (empty? L) '() (rest L))])
    (cond
      [(empty? li) '()]
      [(empty? (rest li)) '()]
      [(cons (first li) (rid-inner (rest li)))])))

To those unfamiliar with Racket, Scheme, or a related Lisp, the named let in rid3 may be more cryptic at first since it is really doing two or three things at once. You can find the docs for it here . Don't be fooled though, it works exactly the same as rid2 . Named let exists precisely because this pattern is so common.

Just for fun - In Racket you can solve this problem without using explicit recursion. Always try to use existing procedures to solve your problems:

(define (rid L)
  (rest (drop-right L 1)))

(rid '(1 2 3 4 5 6))
=> '(2 3 4 5)
(define (rid L)
  (if (< (length L) 3)
      '()
      (reverse (rest (reverse (rest L))))))

;;; recursion inside and more general
;;; you can setting which position 0~n-1 you want to remove

(define (rid-v2 L)
  (local ((define remove-index-list (list 0 (- (length L) 1)))
          (define (auxf L k)
            (cond
              [(empty? L) '()]
              [(memq k remove-index-list) (auxf (rest L) (+ k 1))]
              [else (cons (first L)
                          (auxf (rest L) (+ k 1)))])))
    (auxf L 0)))

tail call recursive version

(define (rid lst (acc '()))
  (cond ((empty? lst) acc)
        ((empty? (cdr lst)) (cdr (reverse acc)))
        (else (rid (cdr lst) (cons (car lst) acc)))))

with elementar lisp (not the most efficient)

(define (rid1 lst)
  (cdr (reverse (cdr (reverse lst))))

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