dolist — 리스트의 각 요소를 순서대로 처리
dolist — 리스트의 각 요소를 순서대로 처리
리스트의 원소를 처음부터 끝까지 하나씩 꺼내 무언가 해야 하는 일은 정말 흔해요. mapcar로 각 원소를 변환할 수도 있지만, 그 과정에서 다른 작업도 함께 해야 하거나 반환값을 그대로 쓰기 어려울 때는 직접 루프를 도는 게 편해요. dolist는 리스트의 각 원소를 차례로 변수에 묶어가며 본문을 실행하는 반복 매크로예요.
본문
dolist는 list-form을 평가해 리스트를 얻고, 그 리스트의 각 원소를 var에 묶어 본문을 순서대로 실행한 뒤 result-form을 평가해요.
dolist (var list-form [result-form]) declaration* {tag | statement}*
=> result*
result-form을 생략하면 반환값은 nil이에요. 또한 result-form을 처리할 시점에는 var가 nil로 묶여 있다는 점을 기억해 두면 좋아요.
(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-form인 temp-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