Search code examples
recursionlispacl2

Wheres-waldo function in LISP


I am trying to solve problems on LISP and I am stuck with this problem for many days.

"Write a function, called wheres-waldo, that takes a lisp object (i.e., a data structure built from conses) as argument and returns a Lisp expression that extracts the symbol waldo from this object, if it is present"

For example,

E.g: (wheres-waldo '(emerson ralph waldo)) =

OUTPUT: (FIRST (REST (REST '(EMERSON RALPH WALDO))))

E.g: (wheres-waldo '(mentor (ralph waldo emerson) (henry david thoreau))) =

OUTPUT: (FIRST (REST (FIRST (REST 
                 '(MENTOR (RALPH WALDO EMERSON)
                          (HENRY DAVID THOREAU))))))

I have written some recursion for example,

(defun wheres-waldo(lispOBJ)
    (cond ((null lispOBJ) nil)
    (equalp (first lispOBJ) waldo)
    ( t (***stuck here how to write recursion for this***))
)

I found this question from http://ai.eecs.umich.edu/people/wellman/courses/eecs492/f94/MP1.html wheres-waldo. Any help would be appreciated. Thank you.


Solution

  • You need to loop over a list, and if an element is a list, recurse into the sublist, exactly as you would implement a deep search. The only difference is that, in order to produce the required output, you need to carry on the s-expression retracing the functions you used to get there.

    Here is one possible implementation. Note that I have used the more traditional car and cdr instead of first and rest - they are equivalent.

    (defun whereis (who obj &optional (sexp (list 'quote obj)))
      (cond
       ; we found the object - return the s-expr
       ((eq obj who) sexp)
       ; try car and the cdr
       ((and obj (listp obj)) 
        (or (whereis who (car obj) (list 'car sexp))
            (whereis who (cdr obj) (list 'cdr sexp))))))
    

    then:

    ? (whereis 'waldo '(emerson ralph waldo))
    (CAR (CDR (CDR '(EMERSON RALPH WALDO))))
    
    ? (whereis 'waldo '(mentor (ralph waldo emerson) (henry david thoreau)))
    (CAR (CDR (CAR (CDR '(MENTOR (RALPH WALDO EMERSON) (HENRY DAVID THOREAU))))))
    
    ? (whereis 'thoreau '(mentor (ralph waldo emerson) (henry david thoreau)))
    (CAR (CDR (CDR (CAR (CDR (CDR '(MENTOR (RALPH WALDO EMERSON) (HENRY DAVID THOREAU))))))))
    
    ? (whereis 'scotty '(beam me up . scotty))
    (CDR (CDR (CDR '(BEAM ME UP . SCOTTY))))
    
    ? (whereis 'waldo '(emerson ralph))
    NIL
    

    If your element can appear more than once, you could also build a list of results:

    ? (whereis 'c '(a b c d c b a))
    ((CAR (CDR (CDR '(A B C D C B A)))) (CAR (CDR (CDR (CDR (CDR '(A B C D C B A)))))))
    

    with this code:

    (defun whereis (who obj)
      (let ((res nil)) ; the final result
        (labels
            ; sub-function: walks the whole list recursively
            ((sub (obj sexp)
               ; found it - add to result list
               (when (eq obj who) (setf res (cons sexp res)))
               ; try car and cdr
               (when (and obj (listp obj))
                 (sub (cdr obj) (list 'cdr sexp))
                 (sub (car obj) (list 'car sexp)))))
          ; call sub-function
          (sub obj (list 'quote obj)))
        res))