newline after every third word in a list with cl:format
Asked Answered
G

1

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 "  
Gehring answered 22/10, 2013 at 15:38 Comment(2)
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…Contradistinction
…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.Contradistinction
C
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:

Contradistinction answered 22/10, 2013 at 15:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.