2013-04-14 5 views
3

지도를 참조하고지도에서 키 값 쌍을 증가시키는 약간의 Clojure 코드를 작성 중입니다. 나는 ref를 올바르게 사용하고 있다고 생각하지만, 원자에 대해서는 확실하지 않습니다. 스왑을 사용해야합니까? 더 관용적 일까? 나는 STM과 Clojure에 익숙하지 않다.이게 thread-safe/sane으로 보이는가? 내가 뭘 놓치고 있니? 그것은 매우 관용적 Clojure의 아니다Clojure : 원자와 심판의 관용적 사용?

(defn increment-key [ref key] 
    (dosync 
     (if (= (get @ref key) nil) 
      (alter ref assoc key (atom 1)) 
      (alter ref assoc key (atom (inc @(get @ref key))))))) 

(defn -main [& args] 
    (def my-map (ref {})) 
    (increment-key my-map "yellow") 
    (println my-map) 
    (increment-key my-map "yellow") 
    (println my-map)) 

인쇄

$ lein run 
#<[email protected]: {yellow #<[email protected]: 1>}> 
#<[email protected]: {yellow #<[email protected]: 2>}> 
+0

멋진 데 감사합니다. –

답변

6

이 작업을 수행합니다 : 영구 데이터 구조 내에서 변경 가능한 객체를 포함하면 일반적으로 불변의 데이터 구조의 요점을 격파한다.

나는 내부 원자를 완전히 피할 것이고, 키와 연관된 번호를 가지고있을 것이다. 그런 다음 my-map에 포함 된 전체 데이터 구조는 변경되지 않습니다.

스레드 안전성은 실제로 어떻게 사용할 것인지에 달려 있습니다. A ref은 여기에없는 여러 개의 참조 전체에서 트랜잭션을 조정할 필요가있을 때만 필요하기 때문에이 경우에는 과도한 것처럼 보입니다. 아마도 atom으로 당신이하려는 일에 충분할 것입니다. 여기

는 대신 그것을 해결할 수있는 방법은 다음과 같습니다

(defn increment-key [ref key] 
    (swap! ref update-in [key] (fn [n] (if n (inc n) 1)))) 

(def my-map (atom {})) 
(increment-key my-map "yellow") 
(println my-map) ;; => {"yellow" 1} 
(increment-key my-map "yellow") 
(println my-map) ;; => {"yellow" 2} 

편집 : 더 나은 당신의 기능에서 가변성을 유지하고 순수한 함수로 증가 - 키를 정의하는 것입니다.

(defn increment-key [m key] 
    (assoc m key (if-let [n (m key)] (inc n) 1))) 

(def my-map (atom {})) 
(swap! my-map increment-key "yellow") 
(println my-map) ;; => {"yellow" 1} 
(swap! my-map increment-key "yellow") 
(println my-map) ;; => {"yellow" 2} 
+0

감사합니다. 나는 최근에 클로제에 대해 많이 생각해 왔고, imperative 한 세상에서 국가가 필요로하는 것을 어떻게 할 것인지 개념화하려고 노력하고 있습니다. 예를 들어, Naive Bayes 분류 자. 어딘가에, 카운트를 높여야하고, 그런 다음 그 카운트에서 가중치를 계산해야합니다. 클로저 환경에서 본질적으로 "상태있는"것으로 보이는 모델을 생각하고 모델을 만드는 방법에 대한 조언이 있습니까? "주 (state)"를 관리하는 더 좋은 방법은 돌연변이 (refating)를 돌리는 것보다 빠릅니까? 나는 뭔가 더 큰 것을 이해하려고하는 것처럼 느껴지지만 조금 붙어 있습니다 ... –

+3

저는 refs/atoms 주위를 거의 전달하지 않습니다. Clojure의 대부분의 기능은 순수합니다. 순수한 값을 가져 와서 새로운 값을 반환합니다. 나는 세계 상태가 하나의 불변의 데이터 구조 인 전체 게임 (https://github.com/mikera/alchemy)을 썼다. "속임수"는 무언가가 변경된 것으로 오래된 데이터의 업데이트 버전을 반환하는 것으로 함수를 보는 것입니다. – mikera

+0

잠깐만, 변경 가능한 PersistentTreeGrid 인'(lib/setup game)'mutate game이 아니십니까? world.clj : 31 –