Search code examples
schemecontinuationscallccseasoned-schemer

get-first - the-seasoned-schemer


Please take a look at two-in-a-row*? function in chapter 19.

My question is about the (leave '()) in the get-first helper function. Note that (waddle l) will either return '() or an atom, which indicates the list has exhausted or an atom from the list is retrieved.

Without (leave '()) it will still return those two kinds of values, just not use the continuation leave. But the book says without (leave '()) is bad, I just can not see why.

(define two-in-a-row*
  (letrec ([leave id] ; the identity function 
           [fill id]
           [waddle (lambda (l)
                     (cond [(null? l) '()]
                           [(atom? (car l))
                            (begin 
                              (letcc rest
                                     (set! fill rest)
                                     (leave (car l)))
                              (waddle (cdr l)))]
                           [else
                            (begin
                              (waddle (car l))
                              (waddle (cdr l)))]))]
           [get-first (lambda (l)
                        (letcc here
                               (set! leave here)
                               (waddle l)
                               (leave '()) ; why is this part needed???
                               ))]
           [get-next (lambda (l)
                       (letcc here
                              (set! leave here)
                              (fill 'go)))]
           [T? (lambda (a)
                 (let ([n (get-next 'dummy)])
                   (if (atom? n)
                       (or (eq? a n)
                           (T? n))
                       #f)))])
    (lambda (l)
      (let ([fst (get-first l)])
        (if (atom? fst)
            (T? fst)
            #f)))))

Thanks very much.

Another interesting tread about this problem.


Solution

  • So thanks for Will Ness's examples. I went over some even simpler ones. So "what is it so bad about that?" -- without (leave '()) in get-first.

    Short Answer:
    Note that from my code
    i) leave will always be recreated each time when we call get-first or get-next. It will return to either get-first or get-next.
    ii) fill will result to be a chain depending on previous fill, and it will always return to get-first.

    Example
    Input: '(1)

    So we start by evaluating get-first on '(1).
    i) set leave
    ii) start (waddle '(1)
    iii) since 1 is an atom, so set fill to be current continuation. Note if we use fill, then it will go to do (waddle (cdr l)), then it will return to get-first. iv) return to get-first by using leave with return value 1.

    Then we go to eval (T? 1), which will in turn go to run get-next.
    i) set leave
    ii) run fill
    iii) start (waddle '())
    iv) return () from waddle, then return to get-first.

    Note
    1) If we don't have (leave '(), then get-first will return '(), then the two-in-a-row* return #f. So we can get the same answer, but the behavior is not what we want.
    2) If we have it, then note that leave is now the leave created by get-next, thus it is going to transfer '() to get-next.
    3) With more than 1 input in the list when we create fill it will be created based on previous fill, thus result to be a chain depending previous fill.