Search code examples
listlisp

How can I create n dimensional list?


As a beginner, I am struggling with lips, in my program I have a list like :
(((NIL (B) (C) (B)) (A)) (E) (G))

But what I want to construct is n-dimensional list (3-dim in this case):

((B C B)(A)(E G))

I have tried flattening the list but it does not seem to be correct. I will appreciate any help.


Solution

  • As you have not really given the specification of what your program is meant to do, here is something that turns the structure you have into the one you want, on the assumption that something else is giving you this structure.

    Your structure is a cons, the car of which is either null, if there's no more structure, or a structure. The cdr of the structure list of single-element lists and we want those elements.

    I've called the structure a BLOB-TREE and each CDR is a BLOB.

    (defun blob-to-list (blob)
      ;; a blob is a list of single-element lists, and we want the elements
      (mapcar (lambda (e)
                (assert (and (listp e) (null (rest e))))
                (first e))
              blob))
    
    (defun blob-tree-to-list (blobs)
      ;; BLOB-TREE is some horrible tree: what we need to do is split it into
      ;; its car & cdr, and then convert the cdr to a list with
      ;; blob-to-list, then recurse on the car, until we get a null car.
      (labels ((extract-blobs (remains accum)
                 (etypecase remains
                   (null accum)
                   (cons
                    (extract-blobs (car remains) (cons (blob-to-list (cdr remains))
                                                       accum))))))
        (extract-blobs blobs '())))
    

    And now

    > (blob-tree-to-list '(((NIL (B) (C) (B)) (A)) (E) (G)))
    ((b c b) (a) (e g))
    

    I rather doubt that this is actually what you want to do.


    As a check, I wrote a function which takes a list in the form you want and converts it into a blob-tree. You can use this to check that things round-trip properly.

    (defun list-to-blob-tree (l)
      (labels ((list-to-blob (es)
                 (mapcar #'list es))
               (build-blob-tree (tail accum)
                 (if (null tail)
                     accum
                   (build-blob-tree (rest tail)
                                    (cons accum (list-to-blob (first tail)))))))
        (build-blob-tree l '())))
    

    If you really want to deal with things like this (which, in real life, you sometimes have to), a good approach is to write a bunch of accessor functions which let you abstract away the shonky data structures you've been given.

    In this case we can write functions to deal with blobs:

    ;;; Blobs are lists are lists where each element is wrapped in a
    ;;; single-element list
    
    (defun blob->element-list (blob)
      ;; a blob is a list of single-element lists, and we want the elements
      (mapcar (lambda (e)
                (assert (and (listp e) (null (rest e))))
                (first e))
              blob))
    
    (defun element-list->blob (list)
      ;; turn a list into a blob
      (mapcar #'list list))
    

    And another set of functions to deal with blob trees, which (it turns out) are just lists built with their cars & cdrs swapped:

    ;;; Blob trees are lists, built backwards
    ;;;
    
    (deftype blob-tree ()
      '(or cons null))
    
    (defconstant null-blob-tree nil)
    
    (defun blob-tree-car (blob-tree)
      (cdr blob-tree))
    
    (defun blob-tree-cdr (blob-tree)
      (car blob-tree))
    
    (defun blob-tree-cons (car cdr)
      (cons cdr car))
    
    (defun blob-tree-null-p (blob-tree)
      (null blob-tree))
    

    In both cases I've only written the functions I need: there are readers but no writers for instance.

    And now we can write the functions we need in terms of these abstractions:

    (defun blob-tree->element-list (blob-tree)
      (labels ((extract-blobs (tree accum)
                 (assert (typep tree 'blob-tree))
                 (if (blob-tree-null-p tree)
                     accum
                   (extract-blobs (blob-tree-cdr tree)
                                  (cons (blob->element-list (blob-tree-car tree))
                                        accum)))))
        (extract-blobs blob-tree '())))
    
    (defun element-list->blob-tree (el)
      (labels ((build-blob-tree (elt accum)
                 (if (null elt)
                     accum
                   (build-blob-tree (rest elt)
                                    (blob-tree-cons
                                     (element-list->blob (first elt))
                                     accum)))))
        (build-blob-tree el null-blob-tree)))
    

    This means that if the representation changes these two mildly hairy functions don't.