constant-space palindromes


Write a predicate that accepts a list and uses constant space to return true iff the list is a palindrome.

 $ cat lipa.rkt 
 #lang racket 
  
 (define (list-palindrome?-linked l) 
  
   ; Return true iff the given list is a palindrome. 
  
   ; This algorithm uses constant space but quadratic time (repeated,  
   ; diminishing list traversals). 
  
   (define (search start end value) 
  
     ; Return a pointer to the last node in the (sub)list [start end) iff the  
     ; last node contains the given value; otherwise return the empty list. 
      
     (let loop ((start start)) 
       (let ((next-start (cdr start))) 
         (if (eq? next-start end) 
             (if (eq? (car start) value) 
                 start 
                 '()) 
             (loop next-start))))) 
  
   (let loop ((start l) (end '())) 
     (cond 
       ((eq? start end) 
        #t) 
        
       ((eq? (cdr start) end) 
        #t) 
        
       (#t 
        (let* ((next-start (cdr start)) 
               (e (search (cdr start) end (car start)))) 
          (if (null? e) 
            #f 
            (loop next-start e))))))) 
  
  
 ; Passing around pointers is a bit obscure (i.e., I got it wrong the first 
 ; time).  Using indices makes the algorithm more straightforward. 
  
  
 (define (list-palindrome?-indexed l) 
  
   ; Return true iff the given list is a palindrome. 
  
   ; Assuming a non-ridiculous list-ref implementation, this algorithm also 
   ; uses constant space and quadratic time. 
  
   (let loop ((l l) (end-index (- (length l) 1))) 
     (cond 
       ((< end-index 1) 
         #t) 
       ((eq? (list-ref (cdr l) (- end-index 1)) (car l)) 
        (loop (cdr l) (- end-index 2))) 
       (#t 
        #f)))) 
          
  
 (require rackunit "utl.rkt") 
  
 (define (testing-1 f) 
   (for-each (lambda (p) (check-equal? (f (car p)) (cadr p))) 
             '((() #t) 
               ((1) #t) 
               ((1 1) #t) 
               ((1 2) #f) 
               ((1 2 1) #t) 
               ((1 1 2) #f)))) 
  
 (testing-1 list-palindrome?-linked) 
 (testing-1 list-palindrome?-indexed) 
  
 (define (testing-2 N) 
   (let* ((n (quotient N 2)) 
             (l (append (build-list n (lambda (i) 1)) 
                        (build-list (- N n) (lambda (i) 2)))) 
             (t (lambda (l) 
                  (check-equal? (list-palindrome?-linked l) 
                                (list-palindrome?-indexed l))))) 
     (call-with-list-permutations l t))) 
  
 (testing-2 5) 
 (testing-2 6) 
 (testing-2 7) 
 (testing-2 8) 
  
 $ racket lipa.rkt 
  
 $