Даже если для атома установлено значение 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 полностью объясняет все нюансы различий.