매핑은 시퀀스의 각 원소에 함수를 적용해서 새로운 시퀀스를 만드는 것을 말해요.
mapcar를 쓰면 객체 리스트의 각 원소에 함수를 적용할 수 있어요.
(mapcar #'double '(1 2 3 4)) ; => (2 4 6 8)
첫 번째 인자가 호출할 함수라는 점에 주목해요.
#'는 함수를 지정하는 특별한 인용 문법이에요.
(여기서 'double을 써도 되지만, #'를 쓰는 쪽이 관용적이고 어쩌면 "더 올바른" 방법이에요.)
매핑은 하나의 시퀀스에 함수를 적용하는 데 그치지 않고, 여러 시퀀스에 대해서도 할 수 있어요.
N개의 시퀀스를 전달하면 함수는 N개의 인자를 받아야 해요.
(mapcar #'list '(1 2) '(a b) '("foo" "bar")) ; => ((1 a "foo") (2 b "bar"))
mapcar 외에도 리스트를 매핑하는 다른 함수들이 있어요.
mapcar가 단연 가장 흔히 쓰이지만, 다른 함수들도 알아 두면 좋아요.
maplist는 mapcar와 비슷하게 동작하지만, 원소가 아니라 연속된 하위 리스트에 대해 동작해요.
먼저 리스트 전체에, 그다음 cdr에, 그다음 그 cdr에, 이런 식으로요:
(maplist #'identity '(a b c)) ; => ((A B C) (B C) (C))
mapc와 mapl은 각각 mapcar와 maplist와 비슷하지만, 결과를 모으지 않고 첫 번째 리스트를 반환해요.
이런 함수는 부수 효과를 일으키는 작업에 사용해요.
mapcan과 mapcon은 mapcar와 maplist와 비슷하지만, nconc를 사용해 결과를 평평하게 만든다는 점이 달라요. nconc는 append와 비슷하지만, 마지막 인자를 뺀 나머지 인자를 파괴적으로 변경해요.
매핑은 리스트에만 국한되지 않아요. 어떤 시퀀스든(다른 개념에서 다뤄요) 매핑할 수 있어요.
리스트가 아닌 시퀀스에는 map 함수를 사용해요.
map은 인자를 세 개 이상 받아요:
(map 'list #'double '(1 2 3)) ; => (2 4 6)
(map 'string #'char-upcase "foobar") ; => "FOOBAR"
특별한 경우로, 결과 시퀀스의 타입으로 nil을 주면 nil이 반환돼요.
이 방식은 (위의 mapc와 mapl처럼) 부수 효과를 위해 사용해요.