Lisp format a character a number of times

2019-01-09 15:41发布

问题:

I am looking for a way to output a character a number of times using format. Is this possible? Can someone fill in the _?_'s, so that the example works?

(let ((n 3))
  (format nil "_?_" _?_ #\* _?_ ))

should return

=> "***"

回答1:

It's nice to see so many solutions: ~A, ~<, and ~{ so far.

The ~@{ iteration construct provides a concise solution:

(format nil "~v@{~A~:*~}" 3 #\*)


回答2:

If you use the ~A directive, you can get this in exactly the form that you suggested, i.e.,

(let ((n 3))
  (format nil "_?_" _?_ #\* _?_ ))

with three format arguments. However, if you use ~<, you can actually do this with just two format arguments. If you don't need this string inside of some other string that's already being generated by format, you could also just make the string using make-string.

Using Tilde A (~A)

You could print the character and specify a minimum width and the same character as the padding character. E.g., using ~v,,,vA and two arguments, you can ensure that some number of characters is printed, and what the padding character is.

CL-USER> (let ((n 3))
           (format nil "~v,,,vA"
                   n     ; number of characters that must be printed
                   #\*   ; character to use as padding
                   #\*)) ; character to print with ~A
"***"

CL-USER> (let ((n 3))
           (format nil "~v,,,vA" n #\* #\*)) 
"***"

CL-USER> (let ((n 10))
           (format nil "~v,,,vA" n #\* #\*))
"**********"

This uses the full form of ~A:

~mincol,colinc,minpad,padcharA is the full form of ~A, which allows control of the padding. The string is padded on the right (or on the left if the @ modifier is used) with at least minpad copies of padchar; padding characters are then inserted colinc characters at a time until the total width is at least mincol. The defaults are 0 for mincol and minpad, 1 for colinc, and the space character for padchar.

as well as v:

In place of a prefix parameter to a directive, V (or v) can be used. In this case, format takes an argument from args as a parameter to the directive. The argument should be an integer or character. If the arg used by a V parameter is nil, the effect is as if the parameter had been omitted. # can be used in place of a prefix parameter; it represents the number of args remaining to be processed. When used within a recursive format, in the context of ~? or ~{, the # prefix parameter represents the number of format arguments remaining within the recursive call.

Using Tilde Less Than (~<)

There's also a less commonly used format directive, tilde less than, that's used for justification. it takes a format string and makes s

~mincol,colinc,minpad,padchar<str~>

This justifies the text produced by processing str within a field at least mincol columns wide. str may be divided up into segments with ~;, in which case the spacing is evenly divided between the text segments.

We can (ab)use this by passing an empty format string and just specifying the width and the padding character:

CL-USER> (let ((n 3))
           (format nil "~v,,,v<~>"
                   n     ; width
                   #\*)) ; padding character
"***"

CL-USER> (let ((n 5))
           (format nil "~v,,,v<~>" n #\*))
"*****"

Just make a string

Of course, unless you need this special string inside of some other string that you're already formatting, you should do what wvxvw suggested, and just use make-string:

(make-string 3 :initial-element #\*)

Other alternatives

format is very flexible, and as this and other answers are pointing out, there are lots of ways to do this. I've tried to stick to ones that should do this in one pass and not do explicit iterations, but this can be done with format iterations, too, as Lars Brinkhoff and wvxvw have pointed out.



回答3:

(format nil "~a~:*~a~:*~a~:*" #\*)
"***"

Or elaborating a bit on Joshua Taylor's answer:

(format nil "~v{~a~:*~}" 3 '(#\*))

Would be one way of doing this. Don't be confused by the asterisks in the format directive, they are control characters, not characters being printed.


In terms of efficiency, however, this:

(make-string 3 :initial-element #\*)

would be a preferred way to achieve the same effect.



回答4:

Like the answer of Lars, but we write the character wih ~C, instead of using the printer with ~A:

(format nil "~v@{~C~:*~}" 3 #\*)

Writing a character with something like write-char is a simpler operation than printing a Lisp object. The printer has a lot of context to observe and has to find the right way to print the object. OTOH, something like WRITE-CHAR just writes a single character to a stream.