Это решение позволяет нам использовать FORMAT
для создания строки и иметь переменный разделитель. Цель не состоит в том, чтобы использовать новую строку формата для каждого вызова этой функции. Хороший компилятор Common Lisp также может захотеть скомпилировать заданную фиксированную строку формата - которая не работает, когда строка формата создается во время выполнения. Смотрите макрос formatter
.
(defun %d (stream &rest args)
"internal function, writing the dynamic value of the variable
DELIM to the output STREAM. To be called from inside JOIN."
(declare (ignore args)
(special delim))
(princ delim stream))
(defun join (list delim)
"creates a string, with the elements of list printed and each
element separated by DELIM"
(declare (special delim))
(format nil "~{~a~^~/%d/~:*~}" list))
Пояснение:
"~{ iteration start
~a print element
~^ exit iteration if no more elements
~/%d/ call function %d with one element
~:* move one element backwards
~}" end of iteration command
%d
- это просто «внутренняя» функция, которая не должна вызываться снаружи join
. Как маркер для этого, он имеет префикс %
.
~/foo/
- это способ вызова функции foo
из строки формата .
Переменные delim
объявлены специальными, так что может быть значение для разделителя, переданного в функцию %d
. Поскольку мы не можем заставить Лисп вызывать функцию %d
из FORMAT
с аргументом разделителя, нам нужно получить его откуда-то еще - здесь из динамического связывания, введенного функцией join
.
Единственная цель функции %d
- написать разделитель - он игнорирует аргументы, переданные format
- он использует только аргумент stream
.