유용한 기타 함수와 매크로
유용한 기타 함수와 매크로
Clojure의 코어에는 어느 한 범주에 꼭 들어맞지는 않지만, 실무에서 제법 자주 만나게 되는 함수와 매크로가 몇 가지 있어요. 이 문서에서는 함수 만들기, 출력하기, 정규식 지원이라는 세 갈래로 나눠서, 어떤 도구가 있는지 하나씩 살펴볼게요.
출처: Clojure 공식문서
본문
boolean·비교 함수와 그 밖의 잡다한 도구들
본격적으로 들어가기 전에, 코어가 미리 마련해 둔 기본 도구들을 훑어볼게요.
boolean·비교 함수로는 =(값 비교), ==(숫자 비교), identical?(동일 객체인지), not=(다른지), not, true?, false?, nil?이 있어요. 여기서 =는 값이 같은지, ==는 숫자가 같은지, identical?는 두 참조가 정말 같은 객체인지를 확인한다는 점을 기억하면 나중에 헷갈리지 않아요.
그 밖에 어디에나 쓸 수 있는 것들로는 identity(받은 값을 그대로 돌려주는 함수), dotimes(횟수만큼 반복), time(평가 시간 측정), assert(단언), with-open(리소스 자동 닫기)이 있어요.
함수 만들기
함수를 만드는 방법은 딱 하나가 아니에요. 상황에 맞는 걸 골라 쓸 수 있도록 몇 가지 대표적인 방법을 표로 정리했어요.
| 함수 | 예시 표현 | 반환값 |
|---|---|---|
fn |
(map (fn [x] (+ 2 x)) [1 2 3]) |
(3 4 5) |
#() 리더 매크로 |
(map #(+ 2 %) [1 2 3]) |
(3 4 5) |
partial |
(map (partial + 2) [1 2 3]) |
(3 4 5) |
comp |
(map (comp - *) [2 4 6] [1 2 3]) |
(-2 -8 -18) |
complement |
(map (complement zero?) [3 2 1 0]) |
(true true true false) |
constantly |
(map (constantly 9) [1 2 3]) |
(9 9 9) |
fn이 가장 기본적인 익명 함수 문법이고, #()는 그 축약형이라고 생각하면 쉬워요. partial은 일부 인자를 미리 채워 둔 함수를, comp는 여러 함수를 합성한 함수를 만들어 줘요. complement는 술어의 결과를 뒤집고, constantly는 어떤 입력에도 늘 같은 값을 돌려주는 함수를 만들어요.
출력하기
객체를 출력 스트림 *out*에 찍는 함수가 여럿 있어요. -str로 끝나는 버전들은 *out*을 StringWriter에 묶어서 출력한 뒤, 그 결과를 문자열로 돌려줘요.
pr은 객체를 출력하는데, 여러 개면 공백으로 구분해요. prn은 같은 일을 하면서 마지막에 newline을 붙여요. print와 println은 각각 pr과 prn을 호출하되 *print-readably*(기본값 true)를 nil로 묶어서 호출해요. 이렇게 하면 문자열이 따옴표나 이스케이프 문자 없이 출력되고, 문자의 앞에 붙는 \도 사라져요.
기본적으로 pr과 prn은 리더가 다시 읽을 수 있는 형태로 출력하고, print와 println은 사람이 읽기 좋은 형태로 출력해요. *print-readably*가 nil이 아닐 때는 *print-meta*(기본값 nil)가 메타데이터 출력 여부를 결정해요.
*out*에 출력하는 함수: pr, prn, print, println, newline
문자열로 출력하는 함수: pr-str, prn-str, print-str, println-str, with-out-str
정규식 지원
정규식 패턴은 #"pattern" 리더 매크로로 읽는 시점에 컴파일하거나, re-pattern으로 실행 시점에 컴파일할 수 있어요. 두 경우 모두 java.util.regex.Pattern 객체를 만들어요.
REPL에서 직접 확인해 볼게요.
user=> (re-seq #"[0-9]+" "abs123def345ghi567")
("123" "345" "567")
user=> (re-find #"([-+]?[0-9]+)/([0-9]+)" "22/7")
["22/7" "22" "7"]
user=> (let [[a b c] (re-matches #"([-+]?[0-9]+)/([0-9]+)" "22/7")]
[a b c])
["22/7" "22" "7"]
user=> (re-seq #"(?i)[fq].." "foo bar BAZ QUX quux")
("foo" "QUX" "quu")
re-seq는 패턴과 일치하는 모든 부분을, re-find는 첫 번째 일치를 문자열 벡터로 찾아줘요. re-matches는 전체 문자열이 패턴과 완전히 일치해야 하고, (?i)처럼 인라인 플래그로 대소문자를 무시할 수도 있어요.
관련 함수로는 re-matcher, re-find, re-matches, re-groups, re-seq가 있어요.