Как вырваться из вложенных дозы - PullRequest
9 голосов
/ 16 мая 2010

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

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

(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)))))))

1 Ответ

13 голосов
/ 16 мая 2010

Даже если для атома установлено значение true, ваша версия не сможет остановиться, пока не завершится внутренняя doseq (пока y> x). Он завершит внешний цикл, как только завершится внутренний цикл. Это заканчивается в конце концов, когда я запускаю его. Не уверен, что вы видите.

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

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

(apply - (first answers))

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...