новая строка после каждого третьего слова в списке с cl:format

Как я могу добавить возврат каретки (используя ~%) после каждого третьего аргумента в списке?
Например, теперь у меня есть:

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

Но я бы хотел:

;=> "one two three  
; four five six  
; seven eight nine  
; ten "  

person Martin Mohan    schedule 22.10.2013    source источник
comment
Небольшая придирка (и я отредактировал ее в заголовке): возврат каретки — это не то же самое, что перевод строки. Символ новой строки должен перевести вас на следующую (новую) строку вывода, тогда как возврат каретки вернет вас в начало текущей строки. Маркировка клавиатуры сбивает это с толку (ввод против возврата (каретки)), как и соглашения в текстовых файлах (символ новой строки, символ возврата каретки, новая строка, затем возврат, возврат, затем новая строка). Это на самом деле важно, потому что в некоторых системах вы можете сделать счетчик прогресса «на месте», итеративно записывая возврат каретки, процент завершения, но…   -  person Joshua Taylor    schedule 22.10.2013
comment
… это не будет работать с новой строкой. Например, с SBCL на консоли после выполнения (format t "hello there~cj" #\return) я вижу на экране jello there, а после (format t "hello there~cj" #\newline) вижу две строки, первая из которых hello there, а вторая j.   -  person Joshua Taylor    schedule 22.10.2013


Ответы (1)


Строка формата str внутри ~{str~} может использовать более одного аргумента из списка на каждой итерации. Это означает, что если бы у вас был список аргументов, которые гарантированно делятся на три, вы могли бы использовать строку формата, например ~{~a ~a ~a~%~}. Вот пример:

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

Однако у вас может быть несколько аргументов, которые не делятся на три, и в этом случае вам нужно будет досрочно завершить итерацию. Вы можете использовать директиву формата ~^ для прерывания, если больше нет аргументов. Поскольку вы можете оказаться в этой ситуации после первого или второго аргумента, вы должны добавить один из них после этих мест. Вот примеры случаев с нулем, одним и двумя завершающими аргументами:

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
"

Возможно, вам не нужна эта последняя новая строка, когда количество элементов делится на три, и в этом случае вы также можете добавить ~^ перед новой строкой:

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

Такая конструкция особенно удобна для написания списков с разделителями:

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

Эти директивы формата (и их варианты) более подробно описаны в HyperSpec (на связанной странице есть больше, чем то, что цитируется здесь):

22.3.7.4 Tilde Left-Brace: итерация

~{str~}

Это итерационная конструкция. Аргумент должен быть списком, который используется как набор аргументов, как если бы он был рекурсивным вызовом формат. string str неоднократно используется в качестве управляющей строки. Каждая итерация может принимать в качестве аргументов столько элементов списка, сколько ей нужно; если str использует два аргумента сам по себе, то два элемента list будут использоваться каждый раз в цикле. Если перед каким-либо шагом итерации список пуст, то итерация завершается. Кроме того, если задан префиксный параметр n, то будет не более n повторений обработки str. Наконец, директива ~^ может использоваться для преждевременного завершения итерации.

Вас также могут заинтересовать эти вопросы:

person Joshua Taylor    schedule 22.10.2013
comment
Спасибо за подробный ответ. - person Martin Mohan; 23.10.2013