Lisp Print List

I am having problems with a lisp format function. I have the following list:

((XXX)(XXXXXX)(XXXXXXXXX)) 

and I need to print it in the following format:

 XXX XX XX XX XXXXXXXXX 

Any thoughts on how to achieve this? The format function is a bit confusing, and the HyperSpec documentation seems to do nothing for me. Thanks.

+4
source share
2 answers

Like every format tool, it has its limitations, and it is not suitable for such problems very well. Probably the best thing you can get in the usual format without resorting to black magic tricks with ~? or ~/ , which you or someone else probably will not understand in the future is the code:

 CL-USER> (format t "~{~{~A ~}~%~}" '((XXX) (XXXXXX) (XXXXXXXXX))) XXXXXXXXXXXXXXXXXX 

If you want a complex output structure, try preprocessing. For example, if the list format 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 li (+ i (/ (length l) 3)))))) '((XXX) (XXXXXX) (XXXXXXXXX)))) 

Here we first collect the list items in the same number of groups for each list, print them and thus get 3 lists with the same number of items, which can then be processed using format .

You can learn more about format in the relevant chapter of Peter Seibel excelent Lisp book: http://gigamonkeys.com/book/a-few-format-recipes.html

EDIT

If you have a variable number of lists, and each of them is twice as large as the previous one, you also need to prepare a format string in advance:

 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 li (+ i len/3)))))) list))) CL-USER> (format-custom-list '((XXX) (XXXXXX) (XXXXXXXXX) (XXXXXXXXXXXX))) XXXXXXXXXXXXXXXXXXXXX XXXXXXXXX NIL 

(The final nil is the format output, which is not output to the output stream t . If you want to get a string from this function, use nil as the format output stream.)

+3
source

I assume that you want to print each list by inserting spaces so that the items match the maximum length of the list.

Although I believe that you can print this with almost one format call, it is better to split the print 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 '((XXX)(XXXXXX)(XXXXXXXXX))) (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 the X + Space * (NumRows - CurrentRowNumber) condition X + Space * (NumRows - CurrentRowNumber) you can use the following function instead of the last two lines in my source code (in a functional style, you can also use a 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))) 
+2
source

Source: https://habr.com/ru/post/1380631/


All Articles