Search code examples
lispsymbolsnullcons

LISP program not small tweaks needed.


The Program is supposed to find each symbol in the List, that comes after a certain symbol. The function gets to parameters passed in. A List which could contain nested-lists and a symbol. The function has to scan thru the list and search for the given symbol and print the symbols that come after the given symbol.

Examples:

(find-all 'a '((b a) ((c a b)))) --> (c b)
 (find-all 'a '(b (a a) c)) --> (a c)
 (find-all 'a '(b d c e)) --> nil

My Code So Far:

(defun find-all (a list)
    (if (consp list)
        (if (consp (car list))
            (find-all a (car list))
        (if (eq a (car list))
            (cons (car(cdr list)) (find-all a(cdr list)))
            (find-all a(cdr list))))))

This code works except when the symbol its looking for is the last atom in the list. it fails in these test cases:

 (find-all 'a '((b a) ((c a b)))) --> (c b)
 (find-all 'a '(b (a a) c)) --> (a c)

but works fine in these cases:

(find-all 'a '(b a c a e)) --> (c e)

The issue is probably at my cons statement and i am unable to fix this.


Solution

  • I don't think your code is correct. First of all, it's not correctly indented, which makes it difficult to read. The correct indentation should be:

    (defun find-all (a list)
      (if (consp list)
          (if (consp (car list))
              (find-all a (car list))
              (if (eq a (car list)) ; if properly intended here
                  (cons (car(cdr list)) (find-all a(cdr list)))
                  (find-all a(cdr list)))))))))
    

    Even after that I have trouble following your logic. For example, when something is a cons, then you should process both the car and the cdr, but you don't. I didn't go through the the debugging process, but you should.


    Instead, I'd like to show you an alternative. I would suggest splitting the problem in 2 parts:

    flattening the list

    Since we start with a nested list but end up with a flat list, it's easier to flatten the list first. Here is a classical flatten function:

    (defun flatten (sxp)
      (labels
       ((sub (sxp res)
          (cond
           ((null sxp)  res)
           ((consp sxp) (sub (car sxp) (sub (cdr sxp) res)))
           (t           (cons sxp res)))))
       (sub sxp nil)))
    

    processing the flat list

    Now, with a flat list, the rest becomes obvious, using the member function (and calling my function find-from to distinguish it from yours at the REPL):

    (defun find-from (a lst)
      (labels
          ((sub (lst)
             (when lst
               (let ((rst (cdr (member a lst))))
                 (when rst
                   (cons (car rst) (sub rst)))))))
        (sub (flatten lst))))
    

    testing

    ? (find-from 'a '((b a) ((c a b))))
    (C B)
    ? (find-from 'a '(b (a a) c))  
    (A C)
    ? (find-from 'a '(b d c e)) 
    NIL
    ? (find-from 'a '(b a c a e)) 
    (C E)