5

How can I to add a carriage return (using ~%) after every third argument in a list?
E.g., I have now:

(format nil "~{~a ~}" (list '"one" '"two" '"three" '"four" '"five" '"six" '"seven" '"eight" '"nine" '"ten"))  
;=> "one two three four five six seven eight nine ten " 

But I would like:

;=> "one two three  
; four five six  
; seven eight nine  
; ten "  
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
Martin Mohan
  • 1,366
  • 12
  • 8
  • Minor nitpick (and I edited it in the title): carriage returns are not the same as newlines. A newline character should put you on the next (new) _line_ of the output, whereas a carriage return will _return_ you to the beginning of the current line. Keyboard labeling confuses this (enter vs. (carriage) return), as do conventions in text files (newline character, carriage return character, newline then return, return then newline). This is actually important, because in some systems you could do an 'in-place' progress counter by iteratively writing "carriage return, percent complete", but… – Joshua Taylor Oct 22 '13 at 16:22
  • …this won't work with a newline. E.g., with SBCL on the console, after executing `(format t "hello there~cj" #\return)`, I see `jello there` on the screen, but after `(format t "hello there~cj" #\newline)`, I see two lines, the first of which is `hello there` and the second of which is `j`. – Joshua Taylor Oct 22 '13 at 16:23

1 Answers1

17

The format string str within ~{str~} can use up more than one argument from the list on each iteration. This means that if you had a list of arguments guaranteed to be divisible by three, you could use a format string like ~{~a ~a ~a~%~}. Here's an example:

CL-USER> (format nil "~{~a ~a ~a~%~}" '(1 2 3 4 5 6))
"1 2 3
4 5 6
"

You might have a number of arguments that's not divisible by three, though, in which case you'd need to terminate an iteration early. You can use the format directive ~^ to break if there are no more arguments. Since you might end up in this situation after the first or second argument, you should add one of these after those places. Here are examples for cases with zero, one, and two trailing arguments:

CL-USER> (format nil "~{~a~^ ~a~^ ~a~%~}" '(1 2 3 4))
"1 2 3
4"
CL-USER> (format nil "~{~a~^ ~a~^ ~a~%~}" '(1 2 3 4 5))
"1 2 3
4 5"
CL-USER> (format nil "~{~a~^ ~a~^ ~a~%~}" '(1 2 3 4 5 6))
"1 2 3
4 5 6
"

You might not want that final newline when there the number of elements is divisible by three, in which case you can add a ~^ before the newline, too:

CL-USER> (format nil "~{~a~^ ~a~^ ~a~^~%~}" '(1 2 3 4 5 6))
"1 2 3
4 5 6"

This kind of construct is particularly nice for writing delimited lists:

CL-USER> (format nil "write(~{~a~^,~})" '("fd" "buf" "count"))
"write(fd,buf,count)"

These format directives (and their variants) are described in more detail in the HyperSpec (there's more in the linked page than what's quoted here):

22.3.7.4 Tilde Left-Brace: Iteration

~{str~}

This is an iteration construct. The argument should be a list, which is used as a set of arguments as if for a recursive call to format. The string str is used repeatedly as the control string. Each iteration can absorb as many elements of the list as it likes as arguments; if str uses up two arguments by itself, then two elements of the list will get used up each time around the loop. If before any iteration step the list is empty, then the iteration is terminated. Also, if a prefix parameter n is given, then there will be at most n repetitions of processing of str. Finally, the ~^ directive can be used to terminate the iteration prematurely.

You might also be interested in these questions:

Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353