Инструменты пользователя

Инструменты сайта


examination:flp:question43

Назначение предиката FORMAT в РС – Лиспе.

Функция FORMAT вместе с расширенным макросом LOOP - одна из двух возможностей Common Lisp, которые вызывают сильную эмоциональную реакцию у многих пользователей Common Lisp. Некоторые их любят, другие ненавидят 1)

Поклонники FORMAT любят ее за мощь и краткость, в то время как противники ее ненавидят за потенциал для возникновения ошибок и непрозрачность. Сложные управляющие строки FORMAT имеют иногда подозрительное сходство с помехами на экране 2), но FORMAT остается популярным среди программистов на Common Lisp, которые хотят формировать небольшие куски удобочитаемого текста без необходимости нагромождать кучи формирующего вывод кода. Хотя управляющие строки FORMAT могут быть весьма замысловаты, но во всяком случае единственное FORMAT-выражение не сильно замусорит ваш код. Предположим например, что вы хотите напечатать значения в списке, разделенные запятыми. Вы можете написать так:

(loop for cons on list

  do (format t "~a" (car cons))
  when (cdr cons) do (format t ", "))

Это не очень плохо, но любой, кто будет читать этот код, должен будет мысленно проанализировать его, прежде чем понять, что все, что он делает - это печать содержимого списка list на стандартный вывод. С другой стороны, вы можете с одного взгляда определить, что следующее выражение печатает список в некоторой форме на стандартный вывод:

  (format t "~{~a~^, ~}" list)

Если вам важна форма, которую примет вывод, тогда вы можете изучить управляющую строку, но если все, что вы хотите - это приблизительно знать, что делает эта строка кода, то это можно увидеть сразу.

Во всяком случае, вам следует по меньшей мере зрительно воспринимать FORMAT, а еще лучше разобраться, на что она способна, прежде, чем вы примкнете к про- или анти-FORMAT'овскому лагерю. Также важно понимать по меньшей мере основы FORMAT, поскольку другие стандартные функции, такие, как функции выбрасывания условий, обсуждаемые в следующей главе, используют управляющие строки в стиле FORMAT для формирования вывода.

Чтобы сильнее запутать дело, FORMAT поддерживает три совершенно разных вида форматирования: печать таблиц с данными, структурная печать (pretty printing) s-выражений, и формирование удобочитаемых сообщений со вставленными FIXME (interpolated? вложенными?) значениями. Печать таблиц с текстовыми данными на сегодня несколько устарела; это одно из напоминаний, что Lisp стар, как FORTRAN. В действительности, некоторые директивы, которые вы можете использовать для печати значений с плавающей точкой внутри полей с фиксированной длинной были основаны прямо на edit descriptors FIXME (дескрипторах редактирования?) FORTRAN, которые использовались в FORTRAN для чтения и печати столбцов с данными, расположенными внутри полей с фиксированной длинной. Тем не менее, использование Common Lisp как замены FORTRAN выходят за рамки этой книги, так что я не буду обсуждать эти аспекты FORMAT.

Структурная печать также находится за рамками этой книги - не потому, что она устарела, а потому, что это слишком большая тема. Вкратце, механизм структурной печати Common Lisp - это настраиваемая система для печати блочно-структурированных данных, включая, но не ограничиваясь, s-выражениями, применяющаяся, когда необходимы переменные отступы и динамически увеличивающиеся переводы строк. Это отличный инструмент при необходимости, но не часто требуемый в повседневном программировании. 3)

Вместо этого я сосредоточусь на частях FORMAT, которые вы можете использовать, чтобы формировать удобочитаемые строки со вставленными в них значениями. Даже ограничивая обзор таким образом, остается изрядное количество материала. Не чувствуйте себя так, как будто вы обязаны помнить каждую деталь, описанную в этой главе. Вы можете достичь многого с лишь несколькими идиомами FORMAT. Сначала я опишу наиболее важные возможности FORMAT; а вам решать, насколько вы хотите стать волшебником FORMAT.

Функция FORMAT Как вы видели в предыдущих главах, функция FORMAT принимает два аргумента: получатель для своего вывода, и управляющую строку, которая содержит буквенный текст и вложенные директивы. Любые дополнительные аргументы предоставляют значения, используемые директивами управляющей строки, которые вставляют эти значения в печатаемый текст. Я буду ссылаться на эти аргументы как на аргументы формата.

Первым аргументом FORMAT, получателем для печатаемого текста, может быть T, NIL, поток, или строка с указателем заполнения. Т обозначает поток *STANDARD-OUTPUT*, в то время как NIL заставляет FORMAT сформировать свой вывод в виде строки, которую функция затем возвращает 4) Если получатель - поток, то вывод пишется в поток. А если получатель - строка с указателем заполнения, то форматированный вывод добавляется к концу строки и указатель заполнения соответственно выравнивается. За исключением случая, когда получатель - NIL и функция возвращает строку, FORMAT возвращает NIL.

Второй аргумент - управляющая строка, является, в сущности, программой на языке FORMAT. Язык FORMAT не целиком «лисповый» - его основной синтаксис основан на символах, а не на s-выражениях, и оптимизирован для краткости, а не для легкого понимания. Вот почему сложные управляющие строки FORMAT могут быть приняты за помехи.

Большинство из директив FORMAT просто вставляют аргумент внутрь выводимого текста в той или иной форме. Некоторые директивы, такие как ~%, которая заставляет FORMAT выполнить перевод строки, не используют никаких аргументов. Другие, как вы увидите, могут использовать более одного аргумента. Одна из директив даже позволяет вам прыгать по списку аргументов, с целью обработки одного и того же аргумента несколько раз, или в некоторых ситуациях пропустить определенные аргументы. Но прежде, чем я буду обсуждать конкретные директивы, давайте взглянем на общий синтаксис директив.

Директивы FORMAT Все директивы начинаются с тильды (~) и кончаются отдельным знаком, который идентифицирует директиву. Вы можете писать этот символ как в верхнем, так и в нижнем регистре. Некоторые директивы принимают префиксные параметры, которые пишутся непосредственно после тильды, разделяются запятыми, и используются для управления такими деталями, как - сколько разрядов печатать после десятичной точки при печати числа с плавающей точкой. Например, директива ~$, одна из директив, использующихся для печати значений c плавающей точкой, по умолчанию печатает два разряда, следующие за десятичной точкой.

CL-USER> (format t «~$» pi) 3.14 NIL Тем не менее с префиксным параметром вы можете указать чтобы функция печатала свой аргумент, к примеру, с пятью десятичными знаками, как здесь:

CL-USER> (format t «~5$» pi) 3.14159 NIL Значениями префиксного параметра являются либо числа, записанные как десятичные, или знаки, записанные в виде одинарной кавычки, за которой следует нужный символ. Значение префиксного параметра может быть также получено из аргумента формата двумя способами: префиксный параметр v заставляет FORMAT использовать один аргумент формата и назначить его значение префиксному параметру. Префиксный параметр # будет вычислен как количество оставшихся аргументов формата. Например:

CL-USER> (format t «~v$» 3 pi) 3.142 NIL CL-USER> (format t «~#$» pi) 3.1 NIL Я дам более правдоподобные примеры использования аргумента # в разделе «Условное форматирование».

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

CL-USER> (format t «~,5f» pi) 3.14159 NIL Также вы можете изменить поведение некоторых директив при помощи модификаторов двоеточие и знака @, которые ставятся после любого префиксного параметра и до идентифицирующего директиву знака. Эти модификаторы незначительно меняют поведение директивы. Например, с модификатором двоеточие, директива ~D, использующаяся для вывода целых чисел в десятичном виде, создает число с запятыми, разделяющими каждые три разряда, в то время как знак @ заставляет ~D включить знак плюс в случае положительного числа.

CL-USER> (format t «~d» 1000000) 1000000 NIL CL-USER> (format t «~:d» 1000000) 1,000,000 NIL CL-USER> (format t «~@d» 1000000) +1000000 NIL В случае необходимости вы можете объединить модификаторы двоеточие и @, для того чтобы получить оба варианта:

CL-USER> (format t «~:@d» 1000000) +1,000,000 NIL В директивах, где оба модифицированных варианта поведения не могут быть осмысленно объединены, использование обоих модификаторов либо не определено или приобретает третье значение.

examination/flp/question43.txt · Последние изменения: 2014/01/15 12:17 (внешнее изменение)