В программировании часто возникает задача собрать строку из значений, которые не являются строками сами по себе. Для решения подобных задач применяют форматирование (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
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1#lang racket
2
3(require (only-in rackunit check-equal? test-begin))
4(require "index.rkt")
5
6(test-begin
7 (displayln (add 0 0))
8 (check-equal?
9 (add 0 0)
10 "+0
11 0
12 -
13 0")
14
15 (displayln "")
16 (displayln (add 1 9))
17 (check-equal?
18 (add 1 9)
19 "+ 1
20 9
21 --
22 10")
23
24 (displayln "")
25 (displayln (add 999 99001))
26 (check-equal?
27 (add 999 99001)
28 "+ 999
29 99001
30 ------
31 100000"))
32
Решение учителя откроется через: