dolist — 리스트의 각 요소를 순서대로 처리

dolist — 리스트의 각 요소를 순서대로 처리

리스트의 원소를 처음부터 끝까지 하나씩 꺼내 무언가 해야 하는 일은 정말 흔해요. mapcar로 각 원소를 변환할 수도 있지만, 그 과정에서 다른 작업도 함께 해야 하거나 반환값을 그대로 쓰기 어려울 때는 직접 루프를 도는 게 편해요. dolist는 리스트의 각 원소를 차례로 변수에 묶어가며 본문을 실행하는 반복 매크로예요.

출처: CLHS 공식 문서 - DOLIST

본문

dolistlist-form을 평가해 리스트를 얻고, 그 리스트의 각 원소를 var에 묶어 본문을 순서대로 실행한 뒤 result-form을 평가해요.

dolist (var list-form [result-form]) declaration* {tag | statement}*
=> result*

result-form을 생략하면 반환값은 nil이에요. 또한 result-form을 처리할 시점에는 varnil로 묶여 있다는 점을 기억해 두면 좋아요.

(setq temp-two '()) ; =>  NIL
(dolist (temp-one '(1 2 3 4) temp-two) (push temp-one temp-two))
=>  (4 3 2 1)

push로 매번 리스트 앞쪽에 넣었으니 결과가 뒤집힌 (4 3 2 1)이 나와요. result-formtemp-two가 이 값을 돌려줘요. 본문에서 계산만 하고 반환값이 필요 없을 수도 있어요.

(setq temp-two 0)  ; =>  0
(dolist (temp-one '(1 2 3 4)) (incf temp-two))
=>  NIL
temp-two  ; =>  4

dolist 폼 전체는 nil이라는 이름의 암묵적 블록이 감싸요. 그래서 본문 안에서 return을 쓰면 더 이상 순회하지 않고 즉시 루프를 빠져나가며, 그때 넘긴 값들이 그대로 반환돼요.

dolist의 본문은 암묵적 tagbody라서 본문 안에서 go로 특정 tag가 붙은 문장으로 제어를 옮길 수도 있어요. 한 가지 짚어둘 점은, var의 묶음 범위가 list-form은 포함하지 않지만 result-form은 포함한다는 거예요. 그리고 순회마다 var를 새로 묶을지 한 번만 묶고 이후에는 대입할지는 구현에 따라 달라질 수 있어요.

함께 보기

do, dotimes, tagbody