Формат (Common Lisp)
Format — это функция в Common Lisp , которая может создавать форматированный текст, используя строку формата, аналогичную строке формата printf . Он обеспечивает больше функций, чем printf
, позволяя пользователю выводить числа в различных форматах (включая, например: шестнадцатеричные, двоичные, восьмеричные, римские цифры и английский), применять определенные спецификаторы формата только при определенных условиях, перебирать структуры данных, выводить данные в виде таблицы и даже рекурсивно , звоню format
внутренне для обработки структур данных, которые включают свои собственные предпочтительные строки форматирования. Функционально это происходит из Lisp Machine Lisp Массачусетского технологического института . [1] где он был основан на Multics ioa_
[ нужна ссылка ] .
Спецификация
[ редактировать ]The format
функция определяется синтаксисом [2]
format destination controlString &rest formatArguments
Директивы в управляющей строке интерполируются с использованием аргументов формата, и построенная таким образом последовательность символов записывается в место назначения.
Место назначения
[ редактировать ]Назначением может быть поток, динамическая строка, T
или NIL
постоянный; последний из которых представляет собой особый случай, поскольку он создает, форматирует и возвращает новый строковый объект, в то время как T
относится к стандартному выводу, обычно эквивалентному консоли. Потоки в Common Lisp включают, среди прочего, строковый вывод и файловые потоки; следовательно, будучи способной записывать в такое разнообразие мест назначения, эта функция объединяет возможности, распределенные между отдельными командами в некоторых других языках программирования, таких как C. printf
для вывода на консоль, sprintf
для форматирования строк и fprintf
для записи файлов.
Разнообразие типов назначения иллюстрируется следующими примерами:
;; Prints "1 + 2 = 3" to the standard output and returns ``NIL''.
(format T "1 + 2 = ~d" 3)
;; Creates and returns a new string containing "1 + 2 = 3".
(format NIL "1 + 2 = ~d" 3)
;; Creates and returns a new string containing "1 + 2 = 3".
(with-output-to-string (output)
(format output "1 + 2 = ~d" 3))
;; Writes to the file "outputFile.txt" the string "1 + 2 = 3".
(with-open-file (output "outputFile.txt"
:direction :output
:if-does-not-exist :create
:if-exists :append)
(format output "1 + 2 = ~d" 3))
;; Appends to the dynamic string the string "1 + 2 = 3".
(let ((output-string (make-array 0
:element-type 'character
:adjustable T
:fill-pointer 0)))
(declare (type string output-string))
(format output-string "1 + 2 = ~d" 3)
(the string output-string))
Управляющая строка и аргументы формата
[ редактировать ]Управляющая строка может содержать буквальные символы, а также метасимвол. ~
(тильда), обозначающая директивы формата . В то время как литералы во входных данных отображаются дословно, директивы создают специальный вывод, часто потребляющий один или несколько аргументов формата.
Директивы
[ редактировать ]Директива формата, представленная ~
, за которым следует ноль или более параметров префикса, ноль или более модификаторов и тип директивы. Следовательно, определение директивы должно соответствовать шаблону
~[prefixParameters][modifiers]directiveType
Тип директивы всегда указывается одним символом, без учета регистра в случае букв. Данные, которые будут обработаны директивой формата, если это вообще необходимо, называются ее аргументом формата и могут представлять собой ноль или более объектов любого совместимого типа. Принимаются ли такие данные и в каком количестве, зависит от директивы и возможных модификаторов, примененных к ним. Тип директивы ~%
, например, воздерживается от использования каких-либо аргументов формата, тогда как ~d
ожидает, что будет напечатано ровно одно целое число, и ~@{
, директива, на которую влияет модификатор at-sign, обрабатывает все оставшиеся аргументы.
Следующая директива, ~b
, ожидает один числовой объект из аргументов формата и записывает его двоичный код (основание 2), эквивалентный стандартному выводу.
(format T "~b" 5)
Если конфигурации являются разрешительными, можно указать параметры префикса.
Параметры префикса
[ редактировать ]Префиксные параметры позволяют вводить дополнительную информацию в директиву, с которой можно работать, аналогично работе с параметрами, когда они предоставляются функции. Параметры префикса всегда необязательны и, если они предусмотрены, должны располагаться между вводящими ~
и либо модификаторы, либо, если их нет, тип директивы. Значения разделяются запятыми, но пробелы с обеих сторон не допускаются. Количество и тип этих параметров зависит от директивы и влияния потенциальных модификаторов.
Два конкретных символа могут использоваться в качестве значений параметров префикса с различной интерпретацией: v
или V
действует как заполнитель для целого числа или символа из аргументов формата, который используется и помещается вместо него. Второй специальный символ, #
, заменяется суммой аргументов формата, но с учетом их потребления. Оба v
и #
включить поведение, определяемое динамическим содержимым, введенным в список параметров префикса.
The v
Значение параметра представляет функциональность, эквивалентную переменной в контексте общего программирования. Учитывая этот простой сценарий, чтобы дополнить двоичное представление целого числа слева, 5
как минимум до восьми цифр с нулями, буквальное решение выглядит следующим образом:
(format T "~8,'0b" 5)
Однако первый параметр префикса, управляющий выходной шириной, может быть определен в терминах v
символ, делегирующий указание значения параметра следующему аргументу формата, в нашем случае 8
.
(format T "~v,'0b" 8 5)
Решения такого типа особенно полезны, если части списка параметров префикса описываются переменными или аргументами функции, а не литералами, как в следующем фрагменте кода:
(let ((number-of-digits 8))
(declare (type (integer 0 *) number-of-digits))
(format T "~v,'0b" number-of-digits 5))
Еще более уместно в ситуациях, связанных с внешним вводом, аргумент функции может быть передан в директиву формата:
(defun print-as-hexadecimal (number-to-format number-of-digits)
"Prints the NUMBER-TO-FORMAT in the hexadecimal system (radix 16),
left-padded with zeros to at least NUMBER-OF-DIGITS."
(declare (type number number-to-format))
(declare (type (integer 0 *) number-of-digits))
(format T "~v,'0x" number-of-digits number-to-format))
(print-as-hexadecimal 12 2)
#
в качестве параметра-префикса подсчитываются те аргументы формата, которые еще не обработаны предыдущими директивами, при этом фактически не потребляя ничего из этого списка. Полезность такого динамически вставленного значения преимущественно ограничивается случаями использования, относящимися к условной обработке. Поскольку номер аргумента может быть только целым числом, большим или равным нулю, его значение совпадает со значением индекса в предложениях условного оператора. ~[
директива.
Взаимодействие спец. #
значение параметра префикса с директивой условного выбора ~[
иллюстрируется следующим примером. Условие содержит четыре предложения, доступные через индексы 0, 1, 2 и 3 соответственно. Количество аргументов формата используется как средство получения индекса предложения; для этого вставляем #
в условную директиву, которая позволяет индексу быть параметром префикса. #
вычисляет количество аргументов формата и предлагает это число в качестве индекса выбора. Аргументы, не использованные этим действием, затем доступны и обрабатываются директивами выбранного предложения.
;; Prints "none selected".
(format T "~#[none selected~;one selected: ~a~;two selected: ~a and ~a~:;more selected: ~@{~a~^, ~}~]")
;; Prints "one selected: BUNNY".
(format T "~#[none selected~;one selected: ~a~;two selected: ~a and ~a~:;more selected: ~@{~a~^, ~}~]" 'bunny)
;; Prints "two selected: BUNNY and PIGEON".
(format T "~#[none selected~;one selected: ~a~;two selected: ~a and ~a~:;more selected: ~@{~a~^, ~}~]" 'bunny 'pigeon)
;; Prints "more selected: BUNNY, PIGEON, MOUSE".
(format T "~#[none selected~;one selected: ~a~;two selected: ~a and ~a~:;more selected: ~@{~a~^, ~}~]" 'bunny 'pigeon 'mouse)
Модификаторы
[ редактировать ]Модификаторы действуют как флаги, призванные влиять на поведение директивы. Допуск, величина поведенческой модификации и эффект, как и в случае с префиксными параметрами, зависят от директивы. В некоторых серьезных случаях синтаксис директивы может быть изменен до такой степени, что некоторые параметры префикса станут недействительными; эта сила особенно отличает модификаторы от большинства параметров. Два допустимых символа-модификатора: @
(знак at) и :
(двоеточие), возможно, в комбинации как :@
или @:
.
Следующий пример иллюстрирует довольно мягкий случай влияния на директиву со стороны @
модификатор: он просто гарантирует, что двоичному представлению форматированного числа всегда предшествует знак числа:
(format T "~@b" 5)
Директивы формата
[ редактировать ]Ниже представлен перечень директив формата, включая их полный синтаксис и эффекты модификаторов. [3]
Характер
|
Описание | Полная форма | Модификатор | |||
---|---|---|---|---|---|---|
Никто | :
|
@
|
:@
| |||
~ | Печатает буквальный ~ персонаж. | ~repetitions~ .
|
Принты repetitions раз ~ характер.
|
Неверный | ||
с , С | Печатает один символ. | ~c .
|
Печатает символ аргумента формата без префикса. | Выводит непечатаемые символы. | Добавляет читаемое #\ префикс.
|
Произносит непечатаемые символы и упоминает клавиши Shift. |
% | Печатает безусловную новую строку. | ~repetitions% .
|
Принты repetitions разрывы строк.
|
Неверный | ||
& | Печатает условную новую строку или новую строку. | ~repetitions& .
|
Если пункт назначения не находится в начале новой строки, печатается repetitions разрывы строк; в противном случае печатает repetitions - 1 разрыв строки.
|
Неверный | ||
| | Печатает разделитель страниц. | ~repetitions| .
|
Принты repetitions раз разделитель страниц.
|
Неверный | ||
р , Р | Либо печатает число в указанной системе счисления (радикса), либо записывает его. | ~radix,minColumns,padChar,commaChar,commaIntervalR .
|
Печатает аргумент как число на английском языке. | Записывает аргумент в английских порядковых числах. | Печатает аргумент римскими цифрами, используя обычный римский формат (например, 4 = IV). | Печатает аргумент римскими цифрами, используя старый римский формат (например, 4 = IIII). |
д , Д | Печатает аргумент в десятичной системе счисления (основание = 10). | ~minColumns,padChar,commaChar,commaIntervald .
|
Печатает как десятичное число без + (плюс) знак или разделитель групп.
|
В качестве разделителя групп используются запятые. | Добавляет знак в начале. | Добавляет знак в начале и использует запятые в качестве разделителя групп. |
б , Б | Печатает аргумент в двоичной системе счисления (основание = 2). | ~minColumns,padChar,commaChar,commaIntervalb .
|
Печатает как двоичное число без + (плюс) знак или разделитель групп.
|
В качестве разделителя групп используются запятые. | Добавляет знак в начале. | Добавляет знак в начале и использует запятые в качестве разделителя групп. |
о , ТО | Печатает аргумент в восьмеричном формате (основание = 8). | ~minColumns,padChar,commaChar,commaIntervalo .
|
Печатает как восьмеричное число без + (плюс) знак или разделитель групп.
|
В качестве разделителя групп используются запятые. | Добавляет знак в начале. | Добавляет знак в начале и использует запятые в качестве разделителя групп. |
х , Х | Печатает аргумент в шестнадцатеричном формате (основание = 16). | ~minColumns,padChar,commaChar,commaIntervalx .
|
Печатает как шестнадцатеричное число без + (плюс) знак или разделитель групп.
|
В качестве разделителя групп используются запятые. | Добавляет знак в начале. | Добавляет знак в начале и использует запятые в качестве разделителя групп. |
ж , Ф | Печатает аргумент как число с плавающей запятой в записи с фиксированной запятой. | ~width,numDecimalPlaces,scaleFactor,overflowChar,padCharf .
|
Печатает с фиксированной точкой без + знак (плюс).
|
Неверный | Добавляет знак в начале. | Неверный |
и , И | Печатает аргумент как число с плавающей запятой в экспоненциальной записи. | ~width,numDecimalPlaces,numDigits,scaleFactor,overflowChar,padChar,exponentChare .
|
Печатает как экспоненциальный без + знак (плюс).
|
Неверный | Добавляет знак в начале. | Неверный |
г , Г | Печатает аргумент либо как число с плавающей запятой в формате с фиксированной запятой, либо в экспоненциальной записи, выбирая автоматически. | ~width,numDecimalPlaces,numDigits,scaleFactor,overflowChar,padChar,exponentCharg .
|
Печатает как с фиксированной точкой или экспоненциально без + знак (плюс).
|
Неверный | Добавляет знак в начале. | Неверный |
$ | Печатает аргумент в соответствии с денежными соглашениями. | ~width,numDigits,minWholeDigits,minTotalWidth,padChar$ .
|
Печатает в денежных конвенциях без + (плюс) знак или дополнение.
|
Добавляет знак перед заполнением символов. | Добавляет знак в начале. | Неверный |
а , А | Печатает аргумент в удобной для человека форме. | ~minColumns,colInc,minPad,padChara .
|
Печатает удобные для человека выходные данные без обоснования. | Принты NIL как пустой список () вместо NIL .
|
Колодки слева, а не справа. | Колодки слева и отпечатки NIL как () .
|
с , С | Печатает аргумент способом, совместимым с read функция.
|
~minColumns,colInc,minPad,padChars .
|
Принты read -совместимо без обоснования.
|
Принты NIL как пустой список () вместо NIL .
|
Колодки слева, а не справа. | Колодки слева и отпечатки NIL как () .
|
В , В | Печатает аргумент в соответствии с переменными управления принтером. | ~w .
|
Печатает в соответствии с текущими установленными переменными управления. | Обеспечивает красивую печать. | Игнорирует ограничения уровня печати и длины. | Игнорирует ограничения уровня и длины печати и обеспечивает красивую печать. |
_ | Печатает разрыв строки в соответствии с правилами принтера. | ~_ .
|
Печатает разрыв строки, если превышена одна строка. | Печатает разрыв строки, если ей не предшествовала ни одна строка. | Использует компактный ( скупой ) стиль. | Всегда вставляет разрыв строки. |
< | Обосновывает вывод. | ~minColumns,colInc,minPad,padChar<expression~> .
|
Выравнивание вывода по левому краю. | Добавляет отступы по левому краю (= выравнивает по правому краю). | Добавляет отступы по правому краю (= выравнивает по левому краю). | Центрирует текст. |
я , я | Делает отступ логического блока. | ~i .
|
Начинает отступ с первого символа. | Отступы, начиная с текущей позиции вывода. | Неверный | |
/ | Отправляет операцию форматирования определяемой пользователем функции. Функция должна принимать как минимум четыре параметра:
Кроме того, можно указать ноль или более аргументов, если функция также должна разрешать параметры-префиксы. |
~prefixParams/function/ .
|
Зависит от реализации функции. | |||
т , Т | Перемещает выходной курсор в заданный столбец или на расстояние по горизонтали. | ~columnNumber,columnIncrementt .
|
Переходит к указанному столбцу. | Ориентируется по разделу. | Перемещает курсор относительно текущей позиции. | Ориентация относительно сечения. |
* | Перемещается по аргументам формата. | ~numberOfArgs* .
|
Пропускает numberOfArgs форматировать аргументы.
|
Ходы numberOfArgs назад.
|
Переходит к аргументу по индексу numberOfArgs .
|
Неверный |
[ | Печатает выражение на основе условия. Эти выражения или предложения разделяются ~; директиву, а предложение по умолчанию можно указать, используя ~:; в качестве его ведущего разделителя. Количество разрешенных пунктов зависит от конкретной разновидности этой директивы, указанной ее модификатором или модификаторами. Вся условная часть должна заканчиваться знаком ~] .
|
|
Аргумент формата должен быть целочисленным индексом, отсчитываемым от нуля, а его значение должно соответствовать значению предложения для выбора и печати. | Выбирает первое предложение, если аргумент формата равен NIL , иначе второй.
|
Обрабатывает предложение только в том случае, если аргумент формата равен T , в противном случае пропускает его.
|
Неверный |
{ | Перебирает один или несколько аргументов формата и печатает их. Итерационная часть должна быть закрыта с помощью ~} директива. Если директива ~^ находится внутри закрытой части, любой контент, следующий за ним, используется только в том случае, если текущий элемент не является последним в обработанном списке. Если параметр префикса numberOfRepetitions указан, его значение определяет максимальное количество элементов для обработки; в противном случае все это будет использовано.
|
~numberOfRepetitions{expression~} .
|
Ожидается, что единственным аргументом формата будет список, его элементы используются по порядку вложенными директивами. | Ожидается, что аргумент формата будет списком списков, использующим его подсписки. | Считает все оставшиеся аргументы формата списком и использует их. | Считает все оставшиеся аргументы формата списком подсписков и использует эти подсписки. |
? | Заменяет директиву следующим аргументом, который, как ожидается, будет аргументом формата, используя последующие аргументы формата в новой части. | ~? .
|
Ожидается, что последующий аргумент формата будет списком, элементы которого связаны со вставленной управляющей строкой. | Неверный | Ожидает отдельных аргументов формата вместо их списка для вставленной части, как можно было бы указать обычным способом. | Неверный |
( | Изменяет регистр включенной строки. | ~(expression~) .
|
Преобразует все символы в нижний регистр. | Делает все слова с заглавной буквы. | Делает с заглавной буквы только первое слово, остальные преобразует в нижний регистр. | Преобразует все символы в верхний регистр. |
п , П | Печатает суффикс единственного или множественного числа в зависимости от аргумента числового формата. | ~p .
|
Ничего не печатает, если аргумент равен 1, в противном случае печатает s .
|
Возвращается к последнему использованному аргументу формата, ничего не печатая, если он равен 1, в противном случае печатает s .
|
Печатает y если аргумент равен 1, в противном случае печатается ies .
|
Возвращается к последнему использованному аргументу формата, печатая y если это было 1, в противном случае печать ies .
|
^ | Используется в директиве итерации ~{...~} для прекращения обработки вложенного содержимого, если не следуют дальнейшие аргументы формата.
|
~p1,p2,p3^ .
|
Работает как описано. | Неверный | ||
Новая строка
|
Пропускает или сохраняет разрывы строк и соседние пробелы в многострочной управляющей строке. | ~Newline .
|
Пропускает следующий разрыв строки и соседние пробелы. | Пропускает разрыв строки, следующий сразу за ним, но сохраняет соседние пробелы. | Сохраняет разрыв строки, следующий сразу за ним, но пропускает соседние пробелы. | Неверный |
Пример
[ редактировать ]Пример буквы С printf
вызов следующий:
printf("Color %s, number1 %d, number2 %05d, hex %x, float %5.2f, unsigned value %u.\n",
"red", 123456, 89, 255, 3.14, 250);
Используя Common Lisp, это эквивалентно:
(format t "Color ~A, number1 ~D, number2 ~5,'0D, hex ~X, float ~5,2F, unsigned value ~D.~%"
"red" 123456 89 255 3.14 250)
;; prints: Color red, number1 123456, number2 00089, hex FF, float 3.14, unsigned value 250.
Другим примером может быть печать каждого элемента списка, разделенного запятыми, что можно сделать с помощью ~{ , ~^ и ~ } директивы: [4]
(let ((groceries '(eggs bread butter carrots)))
(format t "~{~A~^, ~}.~%" groceries) ; Prints in uppercase
(format t "~:(~{~A~^, ~}~).~%" groceries)) ; Capitalizes output
;; prints: EGGS, BREAD, BUTTER, CARROTS.
;; prints: Eggs, Bread, Butter, Carrots.
Обратите внимание, что не только список значений перебирает непосредственно format
, но запятые правильно печатаются между элементами, а не после них. Еще более сложным примером может быть распечатка списка с использованием обычной английской формулировки:
(let ((template "The lucky winners were:~#[ none~; ~S~; ~S and ~S~
~:;~@{~#[~; and~] ~S~^,~}~]."))
(format nil template)
;; ⇒ "The lucky winners were: none."
(format nil template 'foo)
;; ⇒ "The lucky winners were: FOO."
(format nil template 'foo 'bar)
;; ⇒ "The lucky winners were: FOO and BAR."
(format nil template 'foo 'bar 'baz)
;; ⇒ "The lucky winners were: FOO, BAR, and BAZ."
(format nil template 'foo 'bar 'baz 'quux)
;; ⇒ "The lucky winners were: FOO, BAR, BAZ, and QUUX."
)
Возможность определить новую директиву через ~/functionName/
предоставляет средства для настройки. В следующем примере реализуется функция, которая печатает входную строку в нижнем, верхнем или обратном регистре, что позволяет также настроить количество повторений.
(defun mydirective (destination
format-argument
colon-modifier-supplied-p
at-sign-modifier-supplied-p
&optional (repetitions 1))
"This function represents a callback suitable as a directive in a
``format'' invocation, expecting a string as its FORMAT-ARGUMENT
to print REPETITIONS number of times to the DESTINATION.
---
The COLON-MODIFIER-SUPPLIED-P and AT-SIGN-MODIFIER-SUPPLIED-P flags
expect a generalized Boolean each, being the representatives of the
``:'' and ``@'' modifiers respectively. Their influence is defined
as follows:
- If no modifier is set, the FORMAT-ARGUMENT is printed without
further modifications.
- If the colon modifier is set, but not the at-sign modifier, the
FORMAT-ARGUMENT is converted into lowercase before printing.
- If the at-modifier is set, but not the colon-modifier, the
FORMAT-ARGUMENT is converted into uppercase before printing.
- If both modifiers are set, the FORMAT-ARGUMENT is reversed before
printing.
---
The number of times the FORMAT-ARGUMENT string is to be printed is
determined by the prefix parameter REPETITIONS, which must be a
non-negative integer number and defaults to one."
(declare (type (or null (eql T) stream string) destination))
(declare (type T format-argument))
(declare (type T colon-modifier-supplied-p))
(declare (type T at-sign-modifier-supplied-p))
(declare (type (integer 0 *) repetitions))
(let ((string-to-print format-argument))
(declare (type string string-to-print))
;; Adjust the STRING-TO-PRINT based upon the modifiers.
(cond
((and colon-modifier-supplied-p at-sign-modifier-supplied-p)
(setf string-to-print (reverse string-to-print)))
(colon-modifier-supplied-p
(setf string-to-print (string-downcase string-to-print)))
(at-sign-modifier-supplied-p
(setf string-to-print (string-upcase string-to-print)))
(T
NIL))
(loop repeat repetitions do
(format destination "~a" string-to-print))))
;; Print "Hello" a single time.
(format T "~/mydirective/" "Hello")
;; Print "Hello" three times.
(format T "~3/mydirective/" "Hello")
;; Print a lowercase "Hello" (= "hello") three times.
(format T "~3:/mydirective/" "Hello")
;; Print an uppercase "Hello" (= "HELLO") three times.
(format T "~3@/mydirective/" "Hello")
;; Print a reversed "Hello" (= "olleH") three times.
(format T "~3:@/mydirective/" "Hello")
Пока format
несколько печально известен своей тенденцией становиться непрозрачным и трудным для чтения, он обеспечивает удивительно краткий, но мощный синтаксис для специализированных и распространенных потребностей. [4]
Доступна сводная таблица Common Lisp FORMAT. [5]
Ссылки
[ редактировать ]- ^ «Руководство по использованию Lisp-машины» (PDF) . п. 85 . Проверено 7 июня 2024 г.
- ^ «CLHS: Функция ФОРМАТ» . www.lispworks.com .
- ^ «CLHS: Раздел 22.3» . www.lispworks.com .
- ^ Перейти обратно: а б 18. Несколько рецептов FORMAT из практического Common Lisp
- ^ Сводная таблица Common Lisp FORMAT
Книги
[ редактировать ]- Common Lisp HyperSpec Раздел 22.3 Форматированный вывод
- Практический Common Lisp Глава 18. Несколько рецептов FORMAT
Для этой статьи необходимы дополнительные или более конкретные категории . ( июнь 2024 г. ) |