Tanay Narkhede
Tanay Narkhede

Reputation: 357

Explaination about a statement in LISP about format function

I have to convert a decimal number to binary in lisp. I came across this code while searching on web .

(defun :bits (value &optional (size 64))
(format t "~v,'~B" size value))

So please explain me what will each attribute of the code will do.

Upvotes: 2

Views: 1005

Answers (2)

Joshua Taylor
Joshua Taylor

Reputation: 85853

Mongus Pong's answer describes the actual behavior of the code you're looking at pretty well, but I think it's always worth mentioning where to find the answer, too. The Common Lisp HyperSpec is the best source of Common Lisp documentation, but there are parts of it that are a little bit hard to read. Sometimes the documentation of directives for format can be a bit dense. In this case, you'll need to see a few sections, because some of the things in your example can apply to more than just the binary directive, ~B.

You'd want to start with 22.3 Formatted Output, which describes the syntax of format strings:

A directive consists of a tilde, optional prefix parameters separated by commas, optional colon and at-sign modifiers, and a single character indicating what kind of directive this is. There is no required ordering between the at-sign and colon modifier. The case of the directive character is ignored. Prefix parameters are notated as signed (sign is optional) decimal numbers, or as a single-quote followed by a character. For example, ~5,'0d can be used to print an integer in decimal radix in five columns with leading zeros, or ~5,'*d to get leading asterisks.

So we're expecting to see a tilde, then (optionally) parameters separated by colons, an (optional) at sign (@), an (optional) colon (:), and the actual prefix directive (which is case sensitive). That means that

~v,'~B

is broken down as

~   ; initial tilde
v   ; prefix parameter (could also be V)
,   ; separator between prefix parameters
'~  ; prefix parameter (character following single quote)
B   ; directive (could also be b)

So we have two prefix parameters: v and ~, and the directive is B. The next paragraph in the documentation describes what v does when it's a prefix parameter:

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.

Now, to find out what ~B does in general, you'll need to see 22.3.2.3 Tilde B: Binary, although it will pretty much redirect you elsewhere:

This is just like ~D but prints in binary radix (radix 2) instead of decimal. The full form is therefore ~mincol,padchar,commachar,comma-intervalB.

That documentation describes the prefix parameters that are accepted (mincol, padchar, commachar, and comma-interval). These are filled in from left to right. The example ~v,'B has two of those, so v is mincol and ' is padchar. But we still need to see 22.3.2.2 Tilde D: Decimal for what each of those mean:

~mincolD uses a column width of mincol; spaces are inserted on the left if the number requires fewer than mincol columns for its digits and sign. If the number doesn't fit in mincol columns, additional columns are used as needed.

~mincol,padcharD uses padchar as the pad character instead of space.

… The : modifier causes commas to be printed between groups of digits; commachar may be used to change the character used as the comma. comma-interval must be an integer and defaults to 3. When the : modifier is given to any of these directives, the commachar is printed between groups of comma-interval digits.

So, mincol, the width of the result is v, which means that it will be read from the list of arguments, and padchar, the padding character, is ~. Thus:

CL-USER> (bits 13 10)
~~~~~~1101                ; 10 characters wide, padded with ~

CL-USER> (bits 1022 10)
1111111110                ; 10 characters wide, no need for padding

CL-USER> (bits 1022 11)   ; 11 characters with, padded with ~
~1111111110

Upvotes: 7

Mongus Pong
Mongus Pong

Reputation: 11477

So (format nil "~B" 23) will output the number in it's binary form :

> (format nil "~B" 23)
"10111"

But we want to specify the size of output string, we can do this by adding the size as a prefix in the format string.

> (format nil "~8B" 23)
"   10111"

But we don't want to pad it with spaces. We want to pad it with ~.

> (format nil "~8,'~B" 23)
"~~~10111"

Now we don't want to hard code the size of the output in the format string, we want this passed in as a parameter. This is where ~v comes in:

> (format nil "~v,'~B" 8 23)
"~~~10111"

Now note I have been passing nil as the second parameter rather than t. Passing nil means format returns the formatted string rather than printing it. You would probably rather do this.

Upvotes: 7

Related Questions