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
Упражнение не проходит проверку — что делать? 😶
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет 🤨
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя 🤔
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно 🙄
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.