Page 1 of 1

problem with &rest

Posted: Sun Feb 08, 2009 12:33 pm
by Ajschylos
Hi,
I need a function which appends either lists and atoms into one list.

I've tried many variations on something like this:

Code: Select all

(defun app-sym ( &rest lst)
  
  (if (null  lst) nil
    (cond
        ((atom (car lst)) (append (list (car lst)) (app-sym (cdr lst))))
        ((listp (car lst)) (append (car lst) (app-sym (cdr lst)))))))
but it doesn't even compile or work.

Can anybody explain to me what are the limits of using &rest within body of function?
i found no answer in "Common Lisp HyperSpec".

I am using LispWorks.

Should I write a macro ??? Brrr.. it is still to difficult for me :-(

A.

Re: problem with &rest

Posted: Sun Feb 08, 2009 1:43 pm
by ramarren
What do you mean by "limits"? The variable is bound to the list of remaining arguments, and within the body of a function it acts as any other variable. In any case, I would suggest iterative solution, maybe like this (if I understand what you want correctly):

Code: Select all

(defun app-sym (&rest arguments)
  (loop for a in arguments
        appending (if (listp a)
                      a
                      (list a))))

Re: problem with &rest

Posted: Sun Feb 08, 2009 1:47 pm
by dmitry_vk
Your function does not work because of infinite recursion. The last call to the function is (app-sym nil), and the lst == '(nil). But (nill '(nil)) == NIL, so recursion is entered again.

Anyway, the short and more approachable way is to write it like this:

Code: Select all

(defun ensure-list (thing)
  (if (listp thing)
    thing
    (list thing)))

(defun app-sym (&rest args)
  (mapcan #'ensure-list args))

(app-sym 1 2 3) => (1 2 3)
(app-sym 1 '(2 abc 123) 4) => (1 2 ABC 123 4)

Re: problem with &rest

Posted: Sun Feb 08, 2009 10:26 pm
by Ajschylos
Hi Ramaren, you seem to understand perfectly well, your solution works like I want,
but I am interested in recursive solution, and I would like to know why my function doesn't work.

Thanks anyway, A.

Re: problem with &rest

Posted: Sun Feb 08, 2009 10:33 pm
by Ajschylos
Dmitry, Your solution looks cute, and it works.

I see that I've run into an infinite loop, but still do not understand why.

The test (if (null lst) nil "else" ... do sth.) should work if the parameter after &rest is an ordinary list as suggests Ramaren, but it does not.

Re: problem with &rest

Posted: Sun Feb 08, 2009 10:53 pm
by dmitry_vk
Ajschylos wrote:I see that I've run into an infinite loop, but still do not understand why.

The test (if (null lst) nil "else" ... do sth.) should work if the parameter after &rest is an ordinary list as suggests Ramaren, but it does not.
The lst after the supposedly-last recursive call is the ordinary list that would contain a single NIL: (NIL). So (null lst) never returns false. lst is the list of arguments. You always call the app-sym with some argument (but the argument may be NIL), and the arguments list is not empty (but may contain NILs) so lst is not null.

Re: problem with &rest

Posted: Mon Feb 09, 2009 2:58 am
by Ajschylos
Ok. dmitri, now I understand my mistake.
Thanks.
A.

Re: problem with &rest

Posted: Mon Feb 09, 2009 3:56 am
by Ajschylos
Now my function works:

Code: Select all

(defun app-sym (&rest l)
        
       
          (labels (( a-s (x)
                     (let ((el (car x)))
                       (if (null x) nil
                         (cond ((listp el) (append el (a-s (cdr x))))
                               ((atom el) (append (list el) (a-s (cdr x))))
                               (T nil))))))
            (a-s l)))    
Surely it's not so efficient as yours, but it let me train some recursion.

Thanks for help.

Re: problem with &rest

Posted: Thu Feb 12, 2009 1:58 pm
by Tom
A slightly different recursive solution. This one will handle any level of nesting in the lists.

Code: Select all

(defun app-sym (&rest lst)
  "Flatten the lists."
  (let ((item (first lst)))
    (cond
      ((null item) nil)
      ((atom item) (cons item (app-sym (rest lst))))
      ((listp item)
       (append (app-sym (first item))
	            (app-sym (rest item))
	            (app-sym (rest lst)))))))

CL-USER> (app-sym '((a b c) d (e f g) h ((i j) (k l))))
(A B C D E F G H I J K L)