loop — 강력하고 간결한 범용 루프

loop — 강력하고 간결한 범용 루프

단순 반복은 dolistdotimes가 잘 해주지만, 순회하면서 값을 모으고(collect), 조건을 걸고(when), 세어야 할 때(count)는 각각을 따로 조합하기 번거로워요. loop는 이런 것들을 하나의 매크로 몸통에서 간결하게 표현해주는 강력한 반복 매크로예요.

출처: CLHS 공식 문서 - LOOP

본문

loop에는 크게 두 가지 형태가 있어요. **단순형(simple form)**은 조건 없이 몸통만 반복해요.

loop compound-form*
=> result*

**확장형(extended form)**이 진짜 힘이에요. 반복 변수, 종료 조건, 결과 누적 등을 선언적인 절(clause)들로 기술합니다.

loop [name-clause] {variable-clause}* {main-clause}*
=> result*

확장형이 받는 절 종류는 대략 이렇게 나뉘어요.

  • with — 반복 변수 초기화
  • for / as — 반복 변수의 진행 방식(리스트 순회, 산술 진행 등)
  • collect / append / nconc — 결과 리스트 누적
  • sum / count / maximize / minimize — 수치 누적
  • if / when / unless — 조건부 실행
  • while / until / repeat / always / never / thereis — 종료·판정 조건
  • initially / finally — 반복 전·후 처리
  • return — 값 반환

먼저 단순형입니다. 실행기처럼 계속 입력을 받는 프로그램을 loop로 끝없이 돌린 뒤 return으로 빠져나가요.

(defun sqrt-advisor ()
  (loop (format t "~&Number: ")
        (let ((n (parse-integer (read-line) :junk-allowed t)))
          (when (not n) (return))
          (format t "~&The square root of ~D is ~D.~%" n (sqrt n)))))
=>  SQRT-ADVISOR

(sqrt-advisor)
>>  Number: 5<NEWLINE>
>>  The square root of 5 is 2.236068.
>>  Number: done<NEWLINE>
=>  NIL

확장형을 쓰면 for로 반복 변수를, while로 종료 조건을, do로 몸통을 한 번에 묶을 수 있어요.

(defun square-advisor ()
  (loop as n = (progn (format t "~&Number: ")
                      (parse-integer (read-line) :junk-allowed t))
        while n
        do (format t "~&The square of ~D is ~D.~%" n (* n n))))

누적 절의 맛은 이 예제에서 바로 드러나요. 1부터 10까지 홀수만 골라 리스트로 모으는데, whencollect 두 절만으로 끝나요.

(loop for n from 1 to 10
      when (oddp n)
      collect n)
=>  (1 3 5 7 9)

for 절이 다루는 진행 방식도 다양해요. 리스트의 각 원소를 돌려면 in, 리스트의 각 cons를 돌려면 on을 쓰고, 벡터를 훑을 땐 across, 해시 테이블의 키·값을 순회할 땐 being the hash-key / hash-value를 써요.

;; 리스트 순회
(loop for x in '(1 2 3) collect (* x x))      ; =>  (1 4 9)

;; 산술 진행
(loop for i from 1 to 5 sum i)                ; =>  15

;; 해시 테이블 순회
(loop for k being the hash-key of table
      collect k)

함께 보기

do, dolist, dotimes, return, go, throw