원자
원자 (Atoms)
원자(atom)는 공유되고 동기적이며 독립적인 상태를 관리하는 방법을 제공해요. ref나 var처럼 레퍼런스 타입(reference type)이에요. 원자는 atom으로 만들고, 그 상태에는 deref/@로 접근해요. ref와 에이전트처럼 원자도 유효성 검사기(validator)를 지원해요. 원자의 값을 바꾸려면 swap!을 사용해요. 더 저수준인 compare-and-set!도 제공돼요. 원자에 대한 변경은 항상 경쟁 조건(race condition)이 발생하지 않도록 보장돼요.
모든 레퍼런스 타입과 마찬가지로, 원자의 의도된 용도는 Clojure의 불변 데이터 구조 중 하나를 담는 거예요. 그리고 ref의 alter나 에이전트의 send처럼, 값을 변경할 때는 이전 값에 함수를 적용하는 방식으로 해요. 이는 swap!에 의해 원자적(atomic)으로 수행돼요. 내부적으로 swap!은 현재 값을 읽고 함수를 적용한 뒤 compare-and-set!로 다시 넣으려고 시도해요. 그 사이에 다른 스레드가 값을 바꿨어서 재시도가 필요할 수도 있는데, 스핀 루프(spin loop)로 재시도해요. 결과적으로 값은 항상 제공된 함수를 최신 값에 적용한 결과가 원자적으로 됩니다. 다만 함수가 여러 번 호출될 수 있으므로, 함수는 부수 효과(side effect)가 없어야 해요.
원자는 다른 어떤 것과도 조정(coordinate)할 필요가 없는 상태를 표현하고, 더구나 동기적으로 변경하고 싶은 상태를 표현하는 데 효율적인 방법이에요 (마찬가지로 독립적이지만 비동기적인 에이전트와는 다르죠). 전형적인 용도는 메모이제이션(memoization)이에요:
(defn memoize [f]
(let [mem (atom {})]
(fn [& args]
(if-let [e (find @mem args)]
(val e)
(let [ret (apply f args)]
(swap! mem assoc args ret)
ret)))))
(defn fib [n]
(if (<= n 1)
n
(+ (fib (dec n)) (fib (- n 2)))))
(time (fib 35))
user=> "Elapsed time: 941.445 msecs"
(def fib (memoize fib))
(time (fib 35))
user=> "Elapsed time: 0.044 msecs"
출처: 원자 (Atoms)
관련 함수 (Related functions)
- 원자 만들기(create): atom
- 원자 조사(examine): deref (
+@+리더 매크로도 참고) - 원자 상태 변경(change state): swap! reset! swap-vals! reset-vals!
- 유효성 검사기(validators): set-validator! get-validator
- 감시자(watchers): add-watch remove-watch