WITH-HASH-TABLE-ITERATOR — 해시 테이블을 순회하는 매크로
WITH-HASH-TABLE-ITERATOR — 해시 테이블을 순회하는 매크로
해시 테이블의 항목들을 하나씩 꺼내 순회하고 싶을 때 쓰는 매크로예요. maphash처럼 콜백 방식도 있지만, 여기서는 몸체 안에서 명시적으로 '다음 항목'을 호출하는 방식으로 순회해요.
시그니처
with-hash-table-iterator (name hash-table) declaration* form* => results*
본문
인자와 값 (Arguments and Values)
name—macrolet의 첫 번째 인자로 쓰기에 적합한 이름.hash-table— 폼; 한 번 평가되어 해시 테이블을 만들어야 해요.declaration—declare식; 평가되지 않아요.forms— 암묵적 progn.results—forms가 돌려준 값.
설명 (Description)
몸체의 어휘 범위 안에서, name을 macrolet으로 정의해요. 그래서 (name)을 연속해서 호출하면, hash-table을 한 번만 평가해 얻은 해시 테이블에서 항목들을 하나씩 돌려줘요.
(name) 호출은 값 세 개를 돌려줘요:
- 항목이 반환되면 true인 일반화된 불(generalized boolean).
- 해시 테이블 항목의 키(key).
- 해시 테이블 항목의 값(value).
(name)을 계속 호출해 모든 항목을 받은 뒤에는 값 하나, 즉 nil만 돌려줘요.
반복의 암묵적 내부 상태를 with-hash-table-iterator 폼의 동적 지속 기간(dynamic extent) 밖으로 가져가는 경우(예: 호출 폼을 감싼 클로저를 돌려주는 경우) 어떻게 될지는 명시되지 않아요.
with-hash-table-iterator 호출은 몇 번이든 중첩될 수 있고, 가장 안쪽 몸체는 로컬로 설치된 모든 매크로를 호출할 수 있어요. 단, 그 매크로들이 모두 서로 다른 이름을 가져야 해요.
예제 (Examples)
다음 함수는 어떤 해시 테이블에서도 t를 돌려줘야 하고, with-hash-table-iterator의 사용이 대응하는 maphash 사용과 맞지 않으면 오류를 신호해요:
(defun test-hash-table-iterator (hash-table)
(let ((all-entries '())
(generated-entries '())
(unique (list nil)))
(maphash #'(lambda (key value) (push (list key value) all-entries))
hash-table)
(with-hash-table-iterator (generator-fn hash-table)
(loop
(multiple-value-bind (more? key value) (generator-fn)
(unless more? (return))
(unless (eql value (gethash key hash-table unique))
(error "Key ~S not found for value ~S" key value))
(push (list key value) generated-entries))))
(unless (= (length all-entries)
(length generated-entries)
(length (union all-entries generated-entries
:key #'car :test (hash-table-test hash-table))))
(error "Generated entries and Maphash entries don't correspond"))
t))
다음은 with-hash-table-iterator로 구현한, 받아들일 만한 maphash 정의가 될 수 있어요:
(defun maphash (function hash-table)
(with-hash-table-iterator (next-entry hash-table)
(loop (multiple-value-bind (more key value) (next-entry)
(unless more (return nil))
(funcall function key value)))))
부수 효과 (Side Effects)
없음.
특이 상황 (Exceptional Situations)
with-hash-table-iterator가 설치한 로컬 함수 name이 일차 값으로 false를 돌려준 뒤에 호출되면 결과는 정의되지 않아요(undefined).
참고 (Notes)
없음.
더 알아보기
- Section 3.6 — 순회 규칙과 부수 효과