penultimate elements


Write some functions returning the nth penultimate list element.

 $ cat nli.rkt 
 #lang racket 
 (require rackunit) 
  
  
 (define (nth-to-last n l) 
  
   ; Return a pair p.  If n is non-negative and l contains 
   ; more than n elements, the car of p is #t and the cdr of 
   ; p is the nth value from the end of l (the last value in 
   ; l is the 0th value from the end of l).  Otherwise the 
   ; car of p is #f and the cdr of p is undefined. 
  
  
   (define (prep) 
  
     ; Return list l shorn of the first n elements.  If n is 
     ; negative, or l contains less than n elements, return 
     ; the empty list. 
      
     (if (< n 0) 
       '() 
       (let loop ((l2 l) (n2 n)) 
         (if (or (null? l2) (= n2 0)) 
           l2 
           (loop (cdr l2) (- n2 1)))))) 
  
  
   (define (get-value l) 
  
     ; Return a pair p.  If l is non-empty, the car of p is 
     ; #t and the cdr of p is the head of l.  Otherwise the 
     ; car of p is #f and the cdr of p is undefined. 
  
     (if (null? l) (cons #f #f) (cons #t (car l)))) 
  
  
   (let ((h (prep))) 
     (if (null? h) 
       (get-value h) 
       (let loop ((tail l) (head h)) 
         (let ((next-head (cdr head))) 
           (if (null? next-head) 
             (get-value tail) 
             (loop (cdr tail) next-head))))))) 
  
  
 (define (penultimate-element l) 
  
   ; Return a pair p.  If l contains more than 1 element, the 
   ; car of p is #t and the cdr of p is the penultimate value 
   ; of l.  Otherwise the car of p is #f and the cdr of p is 
   ; undefined. 
  
   (nth-to-last 1 l)) 
  
  
 (define l '()) 
  
 (check-equal? (car (nth-to-last -1 l)) #f) 
 (check-equal? (car (nth-to-last 0 l)) #f) 
 (check-equal? (car (nth-to-last 1 l)) #f) 
  
 (check-equal? (car (penultimate-element l)) #f) 
  
 (set! l '(a)) 
 (check-equal? (car (nth-to-last -1 l)) #f) 
 (check-equal? (nth-to-last 0 l) '(#t . a)) 
 (check-equal? (car (nth-to-last 1 l)) #f) 
  
 (check-equal? (car (penultimate-element l)) #f) 
  
 (set! l '(a b)) 
 (check-equal? (car (nth-to-last -1 l)) #f) 
 (check-equal? (nth-to-last 0 l) '(#t . b)) 
 (check-equal? (nth-to-last 1 l) '(#t . a)) 
  
 (check-equal? (penultimate-element l) '(#t . a)) 
  
 (check-equal? (car (penultimate-element '())) #f) 
 (check-equal? (car (penultimate-element '(1))) #f) 
 (check-equal? (penultimate-element '(1 2)) '(#t . 1)) 
 (check-equal? (penultimate-element '(1 2 3)) '(#t . 2)) 
 (check-equal? (penultimate-element '(1 2 3 4)) '(#t . 3)) 
 (check-equal? (penultimate-element '(1 2 3 4 5)) '(#t . 4)) 
  
 ; Zero-origin vs one-origin indexing. 
  
 (check-equal? (car (nth-to-last 0 '())) #f) 
 (check-equal? (nth-to-last 0 '(1)) '(#t . 1)) 
 (do ((n 1 (+ n 1))) ((= n 7)) 
   (do ((x 1 (+ x 1))) ((= x 7)) 
     (let ((r (if (<= x n) '(#f . #f) (cons #t (- x n))))) 
       (check-equal? (nth-to-last n (range 1 (+ x 1))) r)))) 
  
 $ mzscheme nli.rkt 
  
 $