0

This is #7 of of 99 Lisp problems: transform a list, possibly holding lists as elements into a `flat' list by replacing each list with its elements (recursively). I have tried several solutions, e.g from #2680864 or from here. They all work, but I run into a problem if I am flattening a list containing a quoted element. E.g.:

> '(a 'b c)
(A 'B C)

> '(a (quote b) c)
(A 'B C)

> (flatten '(a 'b c))
(A QUOTE B C)

In the latter case I would like to get:

(A 'B C)

It seems that the internal representation of ' gets in the way for this task! SBCL, CLISP, ECL, ... they all behave the same way.

Community
  • 1
  • 1
  • 1
    The problem you are experiencing are due to the fact that the quote-symbol (') is converted to (quote ...) when LISP reads the expression. To make it easier to read the output, a corresponding conversion occurs when LISP prints your list. Therefore, there is no way to distinguish the two when letting LISP read it as a list. – Filip May 17 '12 at 15:14

1 Answers1

2

Quoted elements in a list? That usually does not make sense. Why would you have it quoted?

(a b c) is a list of three symbols. Why would you quote elements in the list? like in (a 'b c)? Why? What would be the purpose of the quote?

In Common Lisp ' is a readmacro which expands 'a into (QUOTE A). Since this is a normal list, a typical flatten operation will collect the symbols QUOTE and A into the flat list. This is because a flatten function typicall checks whether something is an atom or not. If you don't want this, your flatten function needs to check if something is an atom or a two-element list with QUOTE as its first symbol.

But as I said above, the default usage is just to flatten symbols, since quoted symbols are usually not useful inside a list. You need to extend the flatten function otherwise.

For example:

(defun flatten (l &key (test #'atom))
  (cond ((null l) nil)
        ((funcall test l) (list l))
        (t (loop for a in l nconc (flatten a :test test)))))


CL-USER > (flatten '(a (('b) c) ('d) )
                   :test (lambda (item)
                           (or (atom item)
                               (and (eq (first item) 'quote)
                                    (null (cddr item))))))

(A (QUOTE B) C (QUOTE D))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • `(defun flatten (l) (cond ((null l) nil) ((atom l) (list l)) ((equal l (list 'QUOTE (second l))) (cdr l)) (t (loop for a in l appending (flatten a)))))` This gives me `(A B C)`. I guess there is no way to get `(A 'B C)` if I have `(flatten '(A 'B C))` I have no control on the list that gets passed to flatten. I think this solves my problem, although there is a slight asymmetry :) – Antonio Bonifati 'Farmboy' May 17 '12 at 19:48
  • @Antonio Bonifati: see the added example – Rainer Joswig May 18 '12 at 07:58