Common Lisp에는 Lisp 데이터를 텍스트로 표현하는 모든 함수를 포함하는 The Printer라는 개념이 있어요. 그런 함수 중 하나가 format이고, 프로그래머가 원하는 대로 데이터의 형식을 지정할 수 있게 해줘요.
format과 Lisp 프린터 전반의 기능은 다루는 범위가 아주 넓은 주제예요. 여기서는 format 사용의 기초를 설명할게요.
format 함수는 최소 두 개의 인자를 받아요. 출력할 스트림과, 무엇을 어떻게 출력할지 정의하는 제어 문자열이에요.
스트림은 그 자체로 하나의 개념이라 다른 곳에서 설명해요. 지금은 출력이 스트림으로 이루어진다는 것, 그리고 이 인자에 쓸 수 있는 두 가지 특별한 값 t와 nil이 있다는 것만 알아두면 돼요. t는 표준 출력(정확히는 변수 *standard-output*이 묶여 있는 스트림)으로 출력하라는 뜻이고, nil은 문자열을 만들어 반환하라는 뜻이에요.
format은 스트림으로 nil을 지정한 뒤의 경우를 빼면 항상 nil로 평가돼요. 이건 단순한 문자열 보간이나 문자열 생성을 하는 흔한 방법이에요.
(format nil "hello world") ; => "hello world"
(format t "hello world") ; => NIL (but "hello world" is printed to standard output)
제어 문자열에는 "format 지시자"를 넣을 수 있어요. 지시자는 무엇을 출력할지, format에 전달된 다른 매개변수를 어떻게 해석할지에 대한 지침을 format에 줘요. 어떤 의미에서 제어 문자열은 format이 해석해서 실행하는 프로그램이에요. 모든 지시자는 ~로 시작하고 그 뒤에 문자가 와요(예를 들어 ~A, ~D, ~&). 문자의 대소문자는 상관없어요.
대부분의 format 지시자는 format이 받은 인자를 출력에 그대로 끼워 넣어요. 이걸 인자를 "소비한다"고 해요. 어떤 지시자는 인자를 소비하지 않고, 어떤 지시자는 하나보다 많이 소비해요. 심지어 인자 목록 안을 건너뛰거나 인자를 건너뛸 수 있게 해주는 지시자도 있어요.
아주 범용적인 format 지시자는 ~A예요. 데이터를 '사람이 읽을 수 있는' 형태로 출력해요.
(format nil "Value = ~a" 10) ; => "10"
(format nil "hello ~a" "world") ; => "hello world"
(format nil "The list is: ~A" (list 1 2 3)) ; => "The list is: (1 2 3)"
또 다른 유용한 format 지시자는 ~S예요. 데이터를 '기계가 읽을 수 있는' 형태로 출력해요. 출력된 결과를 Lisp 데이터로 다시 해석하고 싶을 때 아주 유용해요. 많은 경우 ~A와 똑같아 보이지만, 예를 들어 문자열의 경우 출력에 따옴표가 포함된다는 점에 주목해요(다른 문자열 안에 들어가면 이스케이프돼요). 그래야 그 값을 문자열 매개변수로 다시 읽어들일 수 있어요.
(format nil "~s" 10) ; => "10"
(format nil "~s" "hello world") ; => "\"hello world\""
(format nil "~s" (list 1 2 3)) ; => "(1 2 3)"
아주 유용한 지시자가 두 개 더 있어요. ~%와 ~&예요. 앞의 것은 줄 바꿈 문자를 출력하고, 뒤의 것은 "새 줄"을 출력해요. 차이는 ~%는 항상 줄 바꿈을 출력하는 반면, ~&는 현재 줄의 맨 앞이 아닐 때만 하나를 출력한다는 거예요. 둘 다 인자를 소비하지 않아요.
(format nil "~%new~%lines") ; => "
new
lines"
(format nil "~&new~&lines" ; => "new
lines"
마지막으로 ~~는 리터럴 ~를 출력하는 단순한 지시자예요.
직업을 가진 거의 모든 사람이 그렇듯, Lisp 외계인 Layla도 때때로 다른 사람들에게 정보를 알리기 위해 보고서를 만들어야 할 때가 있어요. Layla는 이 보고서에 쓸 데이터의 서식을 맞추는 일을 도와줄 수 있겠냐고 물어요.
Layla에게 가장 먼저 필요한 것은 format-quarter-value라는 함수예요. 이 함수는 두 개의 매개변수, 즉 달력 분기와 값을 받아서 보고서에 맞게 서식을 지정해요. 결과는 문자열로 평가되어야 해요.
(format-quarter-value "last" 3.14) ; => "The value last quarter: 3.14"
(format-quarter-value "this" 0) ; => "The value this quarter: 0"
Layla는 format-quarter-value 함수에 고맙다고 하면서, 다음에 필요한 함수에서 아주 유용하게 쓰일 거라고 말해요.
다음 보고서는 두 분기의 분기 이름과 값을 두 줄로 보여줘야 해요.
또한 출력은 첫 번째 인자로 주어지는 스트림으로 보내야 해요.
예를 들어 (format-two-quarters t "last" 3.14 "this" 0)는 표준 출력에 다음과 같이 출력해요:
The value last quarter: 3.14
The value this quarter: 0
앞뒤의 빈 줄에 유의해요.
Layla는 Lisp 외계인들이 값을 Lisp 데이터로 읽을 수 있는 방식으로 보는 걸 좋아한다는 점을 말해 주는 걸 잊었어요. 그래서 Lisp 데이터로 읽을 수 있는 새 함수가 필요해요 (이번에도 지정된 스트림으로요):
(format-two-quarters-for-reading nil "last" 3.14 "this" 0) ; =>
"(\"The value last quarter: 3.14\" \"The value this quarter: 0\")"
Exercism에 가입하고 Common Lisp 트랙을 개념 27개연습 문제 92개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.