Is there a format directive for iterating over vectors in Common Lisp?

General Lisp supports many formatting directives. However, I could not find a convenient directive for my problem. Basically, I would like to print a grid of numbers.

Using the list, do the following:

(format t "~{~A|~A|~A~%~^-----~%~}" '(1 2 3 4 5 6 7 8 9)) 1|2|3 ----- 4|5|6 ----- 7|8|9 NIL 

I could not find a similar construction for iterating over vectors. CLtL2 clearly states that ~{...~} expects a list as an argument. In any case, I tried to use a vector, but my Clisp correctly exclaimed about the wrong type of argument. As a workaround, I convert my vector to a drop list using an omnipotent loop .

 (let ((lst (loop for e across '#(1 2 3 4 5 6 7 8 9) collecting e))) (format t "~{~A|~A|~A~%~^-----~%~}" lst)) 1|2|3 ----- 4|5|6 ----- 7|8|9 NIL 

It works, but it hits me like a clumsy workaround. I would prefer not to create tons of temporary lists just for format . Is there a way to iterate vectors directly?

Out of curiosity, is there a reason why format should not support sequences?

+6
source share
3 answers
 (defun pprint-array (stream array &optional colon amp (delimiter #\Space)) (declare (ignore colon amp)) (loop :with first-time = t :for x :across array :unless first-time :do (format stream "~C" delimiter) :end :do (format stream "~S" x) (setf first-time nil))) (format t "~' :@/pprint-array/" #(1 2 3 4)) ; 1 2 3 4 

You can add more arguments (they will be separated by a comma), or you can also handle the colon and ampersand somehow.

Following Svante, we advise here a slightly modified version of this function, it also uses a colon and ampersand as follows: the colon makes it change between prin1 and princ , and at-sign allows you to print nested arrays recursively (it can be even more complicated to also print multidimensional arrays, etc .... but with a limited time here, what is it:

 (defun pprint-array (stream array &optional colon amp (delimiter #\Space) (line #\Newline)) (if amp (loop :with first-time = t :for a :across array :unless first-time :do (when line (write-char line stream)) :end :if (or (typep a 'array) (typep a 'vector)) :do (pprint-array stream a colon amp delimiter line) :else :do (if colon (prin1 a stream) (princ a stream)) :end :do (setf first-time nil)) (loop :with first-time = t :for x :across array :unless first-time :do (when delimiter (write-char delimiter stream)) :end :do (if colon (prin1 x stream) (princ x stream)) (setf first-time nil)))) 
+7
source
  • I would use coerce instead of loop to convert vector to list s.
  • I would not use format + coerce on vectors ; I would iterate over vector directly. This will give more readable (and more efficient) code.
  • The reason format does not support vector is probably historical.
+3
source

You are probably looking for something like:

 (format t "~{~A|~A|~A~%~^-----~%~}" (coerce #(1 2 3 4 5 6 7 8 9) 'list)) 1|2|3 ----- 4|5|6 ----- 7|8|9 NIL 

But I would advise you to hear the answer sds, as this, of course, is not the most efficient and readable way and direct access to the vector.

+1
source

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


All Articles