Racket: Форматирование строк
В программировании часто возникает задача собрать строку из значений, которые не являются строками сами по себе. Для решения подобных задач применяют форматирование (formatting) строк (оно же — "строковая интерполяция"). В Racket средств для форматирования присутствует довольно много, но мы рассмотрим самые популярные.
Функции format
и printf
Функции format
и printf
ожидают в качестве первого аргумента строку-шаблон и несколько значений, которые будут в этот шаблон подставлены. Разница же между функциями в том, что вызов format
вычисляется в строку, а printf
сразу выводит значение на экран (результатом же вычисления будет #<void>
).
Шаблон может содержать произвольный текст, в нужные места которого вставлены следующие последовательности:
~a
, означающая "подставить в читаемом виде"~v
, означающая "вывести как в REPL"~n
, означающая "вставить перевод строки"~~
, означающая "просто вывести ~"
Есть и другие последовательности, о которых можно почитать здесь, но перечисленные выше используются чаще всего.
~a
используется всегда, когда нужно подставить число, строку или строковый символ. При этом в текст не попадут кавычки, обрамляющие строковый литерал и символы будут подставлены в печатаемом виде, а не в виде кода (a
, вместо #\a
).
~v
пригодится тогда, когда нужно вывести некое комплексное значение в таком виде, в каком его отображает интерпретатор и интерактивном режиме. Этот вариант вывода очень полезен при отладке: выведенные значения часто можно скопировать в REPL или в редактор и повторно вычислить.
Назначение ~n
и ~~
довольно очевидно, согласитесь?
Вот несколько примеров применения printf
:
(printf "This is a list: ~v~n" '(1 2 3))
; => This is a list: '(1 2 3)
(printf "This is a string: ~v~n" "hello")
; => This is a string: "hello"
(printf "~a + ~a = ~a~n" 40 2 (+ 40 2))
; => 40 + 2 = 42
(printf "~v is \"~a\"~n" #\! #\!)
; => #\! is "!"
(printf "~v prints as \"~a\"~n" "abc" "abc")
; => "abc" prints as "abc"
(printf "~v prints as \"~a\"~n" '(42) '(42))
; => '(42) prints as "(42)"
Функции ~a
, ~v
Для большинства последовательностей, работающих в шаблонах, есть аналоги в виде одноимённых функций — таких как ~a
и ~v
. Рассмотрим подробнее ~a
, остальные же вы сможете изучить сами.
~a
принимает одно или несколько значений, которые будет преобразованы в строки и объединены в результирующую строку. Кроме перечня значений функция принимает десяток опциональных именованных аргументов, таких как #:separator
, #:min-width
и #:align
(полный список, как обычно, имеется в документации).
Вот как работают само преобразование аргументов и подстановка разделителя:
(~a 1) ; "1"
(~a 1 2 3) ; "123"
(~a 1 "+" 3) ; "1+3"
(~a 1 2 3 #:separator ", ") ; "1, 2, 3"
#:min-width
и #:align
удобны для вывода чисел в столбик:
(~a 42 #:min-width 6) ; "42 "
(~a 42 #:min-width 6 #:align 'center) ; " 42 "
(~a 42 #:min-width 6 #:align 'right) ; " 42"
Слова с одинарной кавычкой в начале это те самые символы, которые по-английски называются "symbols". Symbols будут рассмотрены позднее, пока можете воспринимать их как константы, означающие сами себя — более компактная и быстрая альтернатива строковым константам.
Среди аргументов ~a
есть и такие, которые ограничивают максимальную длину результирующей строки, задающие то, чем заполняются отступы слева и справа. Словом, возможности широки!
Задание
Реализуйте функцию add
, которая должна для двух чисел вычислить сумму в виде "сложения в столбик" (в виде строки). Ширина текста должна подстраиваться под количество разрядов в сумме. Сами аргументы считайте всегда целыми и неотрицательными.
Примеры:
(displayln (add 1 2))
; => +1
; => 2
; => -
; => 3
(displayln (add 1 9))
; => + 1
; => 9
; => --
; => 10
(displayln (add 1223234 56))
; => +1223234
; => 56
; => -------
; => 1223290