Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lisp List Printing

I am having some troubles regarding the lisp format function. I have the following list:

((X X X)(X X X X X X)(X X X X X X X X X)) 

and I need to print it in the following format:

X  X  X
XX XX XX
XXXXXXXXX

Any thoughts on how to achieve this? The format function is kinda confusing and the HyperSpec documentation doesn't seem to do anything for me. Thanks.

like image 304
Luís Jesus Avatar asked Nov 10 '11 18:11

Luís Jesus


2 Answers

Like every tool format has its limitations and it's not suited for such problems very well. Probably the best you can get with plain format without resorting to black magic tricks with ~? or ~/, that you or anyone else probably won't understand in the future, is this code:

CL-USER> (format t "~{~{~A ~}~%~}"
                 '((X X X) (X X X X X X) (X X X X X X X X X)))
X X X 
X X X X X X 
X X X X X X X X X 

If you want to get your sophisticated output structure, try to do some pre-processing. Like, if the format of the list is hard-coded, you can use this:

(format t "~{~{~6A~} ~%~}"
          (mapcar (lambda (l)
                    (loop :for i :from 0 :to (1- (length l)) :by (/ (length l) 3)
                          :collect (format nil "~{~A ~}"
                                           (subseq l i (+ i (/ (length l) 3))))))
                  '((X X X) (X X X X X X) (X X X X X X X X X))))

Here we first collect the items of a list into same number of groups for each list, print them and this way get 3 lists with the same number of elements, which can then be processed by format.

You can find out more about format in the appropriate chapter of Peter Seibel's excelent Lisp book: http://gigamonkeys.com/book/a-few-format-recipes.html

EDIT

If you have a variable number of lists, with each one being twice bigger than the previous one, you'll also need to prepare the format string beforehand:

CL-USER> (defun format-custom-list (list)
           (format t (format nil "~~{~~{~~~DA~~} ~~%~~}" (* 2 (length list)))
                   (mapcar (lambda (l)
                             (let* ((len (length l))
                                    (len/3 (/ len 3)))
                               (loop :for i :from 0 :to (1- len) :by len/3 
                                     :collect (format nil "~{~A ~}"
                                                      (subseq l i (+ i len/3))))))
                           list)))
CL-USER> (format-custom-list '((X X X) (X X X X X X) (X X X X X X X X X)
                               (X X X X X X X X X X X X)))
X       X       X        
X X     X X     X X      
X X X   X X X   X X X    
X X X X X X X X X X X X  
NIL

(The trailing nil is the output of format, which isn't printed to the output stream t. If you want to get a string out of this function use nil as format's output stream.)

like image 118
Vsevolod Dyomkin Avatar answered Oct 17 '22 03:10

Vsevolod Dyomkin


I'm assuming you want to print each list, inserting spaces to make elements fit max list length.

Though I believe it is possible to print this with nearly single format call, it is better to split printing into several functions:

(defun format-list (stream lst space-count)
  (let ((spaces (make-string 5 :initial-element #\Space))) ;; create string of spaces to insert
    (let ((fmt (concatenate 'string "~{~a" spaces "~}~%")) ;; create formatting string
      (format stream fmt lst)))))

(defvar full-list '((X X X)(X X X X X X)(X X X X X X X X X)))
(defvar max-list-length (max (mapcar length full-list)))  ;; find length 
(mapcar 
  #'(lambda (lst) (format-list t lst (/ (- max-list-length (length lst)) (length lst)))) 
  full-list)

UPD.

For X + Space * (NumRows - CurrentRowNumber) condition you can next function instead of 2 last lines in my original code (in functional style, you can also use loop instead of reduce to make it less functional and more CL-like):

(format-list-of-lists (lst)
  (let ((num-rows (length lst))) 
    (reduce #(lambda (cur-row sub-list) (format-list t sub-list (- num-rows cur-row)) (1+ cur-row)) 
            lst)))
like image 26
ffriend Avatar answered Oct 17 '22 02:10

ffriend