APPEND — 리스트 연결

APPEND — 리스트 연결

APPEND는 함수예요. 여러 리스트를 이어 붙일 때 써요. 리스트를 합친 새 리스트를 돌려주는데, 중요한 특징이 하나 있어요. 연결할 리스트들은 변경되지 않고, 마지막 인자를 제외한 나머지는 복사본이 만들어져요. 그리고 마지막 인자는 복사하지 않고 그대로 붙어요.

출처: CLHS Function APPEND

본문

시그니처는 이렇게 생겼어요.

append &rest lists => result
  • lists — 각 인자는 마지막 인자를 제외하고는 전부 정상 리스트(proper list)여야 해요. 마지막 인자는 어떤 객체여도 괜찮아요.
  • result — 결과 객체.

동작을 좀 더 풀어볼게요. append는 각 리스트의 사본들을 순서대로 이어 붙인 새 리스트를 돌려줘요. 원본 리스트들은 그대로 남아요. 마지막 인자를 뺀 나머지 각 리스트의 리스트 구조만 복사되죠. 마지막 인자는 복사되지 않아서, 앞선 리스트들을 연결한 마지막 점 쌍(dotted pair)의 cdr이 되거나, 앞선 비어 있지 않은 리스트가 전혀 없다면 그대로 반환돼요.

예제를 볼게요.

(append '(a b c) '(d e f) '() '(g)) => (A B C D E F G)
(append '(a b c) 'd) => (A B C . D)

두 번째 예제가 핵심이에요. 마지막 인자 d가 리스트가 아니면, 앞선 리스트를 모두 이어 붙인 결과의 꼬리에 점 쌍으로 매달려 (A B C . D)가 돼요. 마지막 인자는 복사하지 않는다는 성질이 여기서 드러나죠.

원본이 안 변하는 것도 확인할 수 있어요.

(setq lst '(a b c)) => (A B C)
(append lst '(d)) => (A B C D)
lst => (A B C)

append는 새 리스트를 만들어 냈을 뿐 lst는 여전히 (A B C)예요. 이 성질 덕분에 원본을 보존하면서 조합할 때 안전해요. 다만 리스트가 커지면 복사 비용이 들기 때문에, 원본을 변경해도 괜찮은 상황이라면 파괴적(destructive) 버전인 nconc를 쓰는 것도 고려할 수 있어요.

예외적으로, 인자를 하나도 주지 않으면 (append)NIL을 반환하고, 리스트가 아닌 객체를 단 하나만 주면 (append 'a)A를 그대로 돌려줘요.

(append) => NIL
(append 'a) => A

관련 함수로는 nconc, concatenate가 있어요.