Search code examples
recursionfunctional-programmingschemeracketcontinuations

Implementing last-non-zero without continuations


last-non-zero takes a list of numbers and return the last cdr whose car is 0.

So, I can implement it using continuations, but how do I do this with natural recursion.

(define last-non-zero
  (lambda (ls)
    (let/cc return
      (letrec
          ((lnz
            (lambda (ls)
              (cond
                ((null? ls) '())
                ((zero? (car ls))      ;; jump out when we get to last 0.
                   (return (lnz (cdr ls))))
                (else 
                   (cons (car ls) (lnz (cdr ls))))))))
        (lnz ls)))))

Solution

  • Here's an obvious version which is not tail-recursive:

    (define (last-non-zero l)
      ;; Return the last cdr of l which does not contain zero
      ;; or #f if there is none
      (cond
        ((null? l)
         #f)
        ((zero? (car l))
         (let ((lnzc (last-non-zero (cdr l))))
           ;; This is (or lnzc (cdr l)) but that makes me feel bad
           (if lnzc
               lnzc
               (cdr l))))
        (else
         (last-non-zero (cdr l)))))
    

    Here is that version turned into a tail-recursive equivalent with also the zero test moved around a bit.

    (define (last-non-zero l)
      (let lnzl ([lt l]
                 [r #f])
        (if (null? lt)
            r
          (lnzl (cdr lt) (if (zero? (car lt)) (cdr lt) r)))))
    

    It's much clearer in this last version that the list is traversed exactly once.