1

Is there a way to do something like the following?

(format t "~{~va~}" '("aa" "bb" "cc") 4)

I need to iterate through a list. Each element of that list should be padded with a variable number of spaces (specified at runtime, so I cannot use "~4a").

Or more generally, is there a way to refer to a specific argument in the argument list of FORMAT?

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Gradient
  • 2,253
  • 6
  • 25
  • 36

3 Answers3

2

By nesting format function, you can do what you want.

(format t (format nil "~~{~~~Aa~~}" 4) '("aa" "bb" "cc"))
;; returns: aa  bb  cc  

Here the inner format directive: The nil as first argument, format returns a string.

(format nil "~~{~~~Aa~~}" 4)
;; returns: "~{~4a~}" - and this is exactly what you want to give
;; to the outer `format` as second argument!

You can of course write a function for this:

(defun format-by-padding-over (lst padding)
  (format t (format nil "~~{~~~Aa~~}" padding) lst))

And then:

(format-by-padding-over '("aa" "bb" "cc") 4)
;; aa  bb  cc  
;; NIL

I learned this trick here from @Sylwester (many thanks!).

Gwang-Jin Kim
  • 9,303
  • 17
  • 30
0

You could also interleave the list with repetitions of the padding:

(format t "~{~va~}"
        (mapcan (lambda (element)
                  (list 4 element))
                list))
Svante
  • 50,694
  • 11
  • 78
  • 122
0

You can build the format control string using nested format functions, but then you have to take care about escaping tildes. When working with regular expressions (using CL-PPCRE), one can define regular expressions using trees, like (:alternation #\\ #\*), which helps preventing bugs and headaches related to escaping special characters. The same can be done with format strings, using format-string-builder, available in Quicklisp:

(lambda (v)
  (make-format-string `((:map () (:str ,v)))))

Returns a closure, which can be used to build format strings:

(funcall * 10)
=> "~{~10a~}"
coredump
  • 37,664
  • 5
  • 43
  • 77