2010-05-15 38 views
9

我有一個關於嵌套循環doseq問題爆發。在啓動函數中,一旦找到答案,我將atom設置爲true,以便外部循環驗證使用: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 s到做到這一點。一個doseq可以一次處理兩個seq。

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真實的。)沒有機制嵌套doseqs,我知道的「爆發」,除了throw/catch,但是這是相當非地道。儘管如此,你根本不需要原子或doseq

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

您的代碼風格非常重要。 「遍歷這些列表,然後測試這些值,然後打印一些內容,然後停止循環。」在Clojure中使用像這樣的控制原子並不是很習慣。

一個功能更強大的方法是採取一個序列(五邊形列表),在把它變成其他seqs直到你得到一個序列,你想要的東西,讓你的功能包裹。首先,我使用for把這種seqs的兩個拷貝到對其中y < = x的一個起。然後,我使用filter將該seq變成了一個篩選出我們不關心的值的篩選器。

filterfor是懶惰的,所以一旦它找到first的有效值,如果只有一個是你想要的,它將停止運行。這將返回您想要的兩個數字,然後您可以將它們相減。

(apply - (first answers)) 

或者你也可以進一步包裝在另一個map功能來計算你的差異。

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

以這種方式在功能上編程有一些優點。該序列被緩存(如果你持有到頭部像我在這裏做的),所以一旦一個值的計算,它會記住它,你可以從此即時訪問它。無需重新設置原子,您的版本就不能再次運行,然後必須重新計算所有內容。隨着我的版本,你可以(take 5 answers)拿到第5個結果,或在地圖的結果,如果你想要做其他事情。您可以在其上打印doseq並打印這些值。等等

我敢肯定還有其他(也許更好)的方式來做到這一點,仍然不使用原子。除非在Clojure中100%必要,否則通常應該避免引用變異。

更改atoms/agents/refs的函數名稱可能因爲機制不同而不同。參考資料是同步的,並通過交易協調。代理是異步的。原子是同步的和不協調的。它們都是「改變參考」,並且可能某種超級功能或宏可以用一個名字來包裝它們,但這會掩蓋他們在底下做了截然不同的事情的事實。充分解釋差異可能超出SO解釋的範圍,但http://clojure.org完全解釋了所有差異的細微差別。

+0

謝謝。沒有意識到我可以在單劑量/每劑量下做「循環」。首先在濾波和解構五邊形列表上使用的技巧非常棒。 – fizbin 2010-05-16 07:35:58