2010-05-15 2 views
9

У меня есть вопрос о вложенных петлях дозы. В функции запуска, как только я нахожу ответ, я устанавливаю атом в true, так что проверка внешнего цикла с помощью: while терпит неудачу. Однако кажется, что он не нарушает его, и петли продолжают двигаться. Что с этим не так?Как вырваться из вложенных доз

Я также довольно запутан с использованием атомов, refs, agents (почему у них разные имена для функций обновления, когда механизм почти такой же?) И т. Д. Можно ли использовать атом в эта ситуация как флаг? Очевидно, мне нужна переменная типа объекта для хранения состояния.

(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 (до у> х). Он завершает внешний цикл, как только внутренний контур завершается. Это заканчивается, когда я запускаю его. Не уверен, что вы видите.

Для этого вам не нужны два doseq. Один doseq может обрабатывать сразу два отсека.

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.

Более функциональный способ - взять seq (пятиугольный список) и обернуть его функциями, которые превращают его в другие секции, пока вы не получите seq, который даст вам то, что вы хотите. Сначала я использую for, чтобы превратить две копии этих seqs в один пара пар, где y < = x. Затем я использую filter, чтобы превратить этот seq в один, который отфильтровывает значения, которые нам не нужны.

filter и for ленивы, так что это прекратит работать, как только оно найдет действительное значение first, если оно все, что вы хотите. Это возвращает два числа, которые вы хотите, и затем вы можете их вычесть.

(apply - (first answers)) 

Или вы можете дополнительно обернуть функцию в другом map для расчета разницы для вас.

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

Существует ряд преимуществ для функционального программирования таким образом. Seq кэшируется (если вы держите голову так, как я здесь), поэтому, как только значение вычисляется, оно запоминает его, и вы можете получить к нему доступ мгновенно с этого момента. Ваша версия не может быть запущена снова без сброса атома, а затем придется перерасчитывать все. С моей версией вы можете (take 5 answers) получить первые 5 результатов или нанести на карту результат, чтобы делать другие вещи, если хотите. Вы можете выполнить doseq и распечатать значения. И т.д. и т. Д.

Я уверен, что есть другие (возможно, лучшие) способы сделать это, не используя атом. Обычно вы должны избегать мутирующих ссылок, если это не нужно на 100% в Clojure.

Названия функций для изменения атомов/агентов/рефлексов различны, вероятно, потому, что механика не то же самое. Refs синхронно и координируется транзакциями. Агенты являются асинхронными. Атомы синхронны и нескоординированы. Они все вроде «меняют ссылку», и, вероятно, какая-то суперфункция или макрос может обернуть их всем одним именем, но это заслонит тот факт, что они делают радикально разные вещи под капотом. Объяснение различий полностью, вероятно, выходит за рамки сообщения SO, чтобы объяснить, но http://clojure.org полностью объясняет все нюансы различий.

+0

Спасибо. Не понял, что я могу сделать «петлю» в одной дозе. Трюк с использованием первого в отфильтрованном и деструктурированном пятиугольном списке велик. – fizbin

Смежные вопросы