Search code examples
listloopsif-statementlispcommon-lisp

Breaking From Loop Through If Statement LISP


I am currently writing a small program in LISP that will take in a list, and split it as follows:

(split '(1 2 3) returns-> ((1 2) (3)) 
(split '(1 2 3 4) returns-> ((1 2) (3 4))

I feel like I am very close and have the basic logic of it written, my issue is that I have to perform 2 - 3 actions if the if statement evaluates to T. Because of this I keep receiving the error

Error(s), warning(s): *** - SYSTEM::%EXPAND-FORM: (PUSH (CAR LST) (CDR (LAST NEWLST))) should be a lambda expression

When the if statement evaluates to true, I need to add the head of the current list to the end of my list variable newLst, then I need to take the tail of the current list and set the value of my other list variable new2Lst to that. After doing this, I need to break out of the loop and append the two lists to each other. This probably does not make much sense, sorry, hopefully the code makes more sense, please find it below. Thank you all in advance for the help, I really appreciate each and every bit of it!

(defun split (lst) 
    (cond ((= (list-length lst) 1) lst) 
          (t   (setq w (list-length lst))
               (setq newLst (list (car lst))) 
               (setq new2Lst '())
               (loop for x from 1 to (+ w 1) do 
                     (if (= x (ceiling (/ w 2))) 
                         ( (push (car lst) (cdr( last newLst))) (setq new2Lst (cdr lst)) (return)) 
                         (push (car lst) (cdr( last newLst)))
                      )  
                     (setq lst (cdr lst))
               ) 
               (append (list newLst) (list new2Lst))
            ) 
     ) 
)

Solution

  • progn

    When you want to have a sequence of expressions a, b, c to evaluate, you need to put them in a (progn a b c) form. But here you had them wrapped in a list of forms: when you evaluate (a b c) in a normal evaluation context, ie. not in macros, special forms, ..., a is expected to be a function being called with arguments b and c. That's why you have that specific error: (push ...) is indeed not a function name or a lambda expression.

    Undefined variables

    You are calling setq on symbols that are not bound to known variables, at least in your function. Most implementations then treat the assignment as an assignment to symbol-value of the symbol, even though the behaviour is unspecified. You should rather declare them in your function by having a let binding:

    (let ((w (length list))
          (... ...))
      ...)
    

    By the way, you compute the length of the list twice in this function, the length could be computed once only, and in fact you only need to know if the list has one element and no list in its cdr: this is a constant-time computation, unlike list-length which needs to traverse the whole list.

    variable names, case

    Be careful about how you name your variables, and please stick to the Lisp conventions by using hyphen-separated-words instead of a mix of upper and lower case.