2010-05-15 5 views
9

중첩 된 doseq 루프에 관한 질문이 있습니다. 시작 함수에서 일단 내가 답을 찾으면 원자를 true로 설정하여 while 루프를 사용하여 외부 루프 유효성 검사를 수행합니다. while은 실패합니다. 그러나 그것은 그것을 깨뜨리지 않는 것, 그리고 루프가 계속됩니다. 그게 뭐가 잘못 됐어?중첩 된 doseqs에서 벗어나는 방법

저는 원자, 심판원, 에이전트의 사용법과도 매우 혼동합니다 (메커니즘이 거의 동일 할 때 업데이트 기능에 다른 이름이 붙는 이유는 무엇입니까?) 원자를 사용할 수 있습니까? 깃발 같은 상황? 분명히 상태를 저장하기 위해 객체와 같은 변수가 필요합니다.

(def pentagonal-list (map (fn [a] (/ (* a (dec (* 3 a))) 2)) (iterate inc 1))) 


(def found (atom false)) 


(defn pentagonal? [a] 
    (let [y (/ (inc (Math/sqrt (inc (* 24 a)))) 6) 
     x (mod (* 10 y) 10)] 
    (if (zero? x) 
    true 
    false))) 


(defn both-pent? [a b] 
    (let [sum (+ b a) 
     diff (- a b)] 
    (if (and (pentagonal? sum) (pentagonal? diff)) 
     true 
     false))) 

(defn start [] 
(doseq [x pentagonal-list :while (false? @found)] 
    (doseq [y pentagonal-list :while (<= y x)] 
     (if (both-pent? x y) 
      (do 
      (reset! found true) 
      (println (- x y))))))) 

답변

13

원자는 true로 설정되어 있어도 일단 버전이 내측 doseq 끝날 때까지 실행을 중지 할 수없는 (Y까지> X). 내부 루프가 끝나면 외부 루프를 종료합니다. 그것은 결국 그것을 끝낼 때 그것을 실행합니다. 당신이보고있는 것이 확실하지 않습니다.

이 작업을 수행하는 데 두 개의 숫자가 필요하지 않습니다. 한 doseq 한 번에 두 seqs 처리 ​​할 수 ​​있습니다.

user> (doseq [x (range 0 2) y (range 3 6)] (prn [x y])) 
[0 3] 
[0 4] 
[0 5] 
[1 3] 
[1 4] 
[1 5] 

(같은이 for의 사실이다.)이 throw/ catch를 제외하고, 내가 알고있는 중첩 된 doseqs의 "탈옥"에 대한 메커니즘이 없다,하지만 오히려 않은 관용적이다. 당신은 원자 또는 doseq을 전혀 필요로하지 않습니다.

(def answers (filter (fn [[x y]] (both-pent? x y)) 
        (for [x pentagonal-list 
          y pentagonal-list :while (<= y x)] 
         [x y]))) 

코드는 매우 스타일에 필수적입니다. "이 목록을 반복하고 값을 테스트 한 다음 무언가를 인쇄 한 다음 반복을 중지하십시오." 이와 같은 제어를 위해 원자를 사용하는 것은 Clojure에서는 그다지 관용적이지 않습니다.

더 기능적인 방법은 seq (pentagonal-list)를 취하여 원하는 seq를 얻을 때까지 다른 seqs로 바꾸는 기능으로 포장하는 것입니다. 먼저 for을 사용하여이 seqs 사본 두 개를 하나의 seq 쌍 (y는 < = x)으로 바꿉니다. 그런 다음 filter을 사용하여 해당 seq를 우리가 신경 쓰지 않는 값을 걸러내는 것으로 바꾸십시오.

filterfor은 게으르므로 first 값이 발견되면 실행이 중지됩니다. 이렇게하면 원하는 두 숫자가 반환 된 다음 빼기 수 있습니다.

(apply - (first answers)) 

또는 당신은 더 당신을위한 차이를 계산하는 다른 map에서 함수를 래핑 할 수 있습니다.

(def answers2 (map #(apply - %) answers)) 
(first answers2) 

이러한 방식으로 프로그래밍하는 데는 몇 가지 이점이 있습니다. seq는 캐시됩니다 (여기에서와 같이 머리를 쥐고 있으면). 값을 계산하면 기억하고 즉시 그 다음에 액세스 할 수 있습니다. 원자를 다시 설정하지 않으면 버전을 다시 실행할 수 없으며 모든 것을 다시 계산해야합니다. 내 버전을 사용하면 (take 5 answers)을 통해 처음 5 개 결과를 얻거나 원하는 결과를 바탕으로 다른 작업을 수행 할 수 있습니다. 위에 doseq을 입력하고 값을 인쇄하십시오. 기타 등등.

원자를 사용하지 않고이 작업을 수행하는 다른 방법이있을 수 있습니다. Clojure에서 100 % 필요한 경우가 아니면 일반적으로 참조를 변경하는 것을 피해야합니다.

원자/에이전트/참조를 변경하기위한 함수 이름은 아마도 메커니즘이 동일하지 않기 때문에 다릅니다. 참조는 동기적이고 트랜잭션을 통해 조율됩니다. 에이전트는 비동기식입니다. 원자는 동기적이고 조정되지 않습니다. 그것들은 모든 종류의 "참조 변경"과 아마 어떤 종류의 수퍼 - 함수 또는 매크로로 그것들을 모두 하나의 이름으로 감쌀 수 있지만, 그것은 그것들이 두포에서 대단히 다른 일을한다는 사실을 모호하게 만듭니다. 차이점을 완전히 설명하는 것은 아마도 설명 할 게시물의 범위를 벗어날 것이지만, http://clojure.org은 차이점의 모든 뉘앙스를 완벽하게 설명합니다.

+0

감사합니다. 내가 하나의 doseq/for에서 "루핑"을 할 수 있다는 것을 깨닫지 못했다. 필터링되고 소멸 된 펜타그램리스트에서 처음으로 사용하는 트릭은 훌륭합니다. – fizbin

관련 문제