我对嵌套剂量q循环有疑问。在start函数中,一旦我找到答案,我将atom设置为true,以便外部循环验证:while失败。然而,似乎它没有打破它,循环继续前进。怎么了?
我也对atom,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)))))))
答案 0 :(得分:13)
即使原子设置为true
,您的版本也无法停止运行,直到内部doseq
完成(直到y&gt; x)。一旦内循环结束,它将终止外循环。当我运行它时它最终会终止。不确定你在看什么。
您不需要两个doseq
来执行此操作。一个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
的情况也是如此。)除了throw
/ catch
之外,没有“突破”我所知的嵌套剂量的机制,但这不是-idiomatic。尽管如此,你根本不需要原子或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的函数中,直到获得一个能够提供所需内容的seq。首先,我使用for
将此seqs的两个副本转换为一对seq,其中y <= x。然后我使用filter
将该seq转换为过滤掉我们不关心的值的那个。
filter
和for
是懒惰的,所以一旦找到first
有效值,这将停止运行,如果你想要的话。这将返回您想要的两个数字,然后您可以减去它们。
(apply - (first answers))
或者您可以在另一个map
中进一步包装该函数,以便为您计算差异。
(def answers2 (map #(apply - %) answers))
(first answers2)
以这种方式进行功能编程有一些优点。 seq被缓存(如果你像我一样按住头部),所以一旦计算出一个值,它就会记住它,然后你可以立即访问它。如果没有重置原子,你的版本将无法再次运行,然后必须重新计算所有内容。使用我的版本,您可以(take 5 answers)
获取前5个结果,或者根据结果映射以执行其他操作(如果需要)。您可以doseq
覆盖它并打印值。等等。
我确信还有其他(可能更好)的方法可以在不使用原子的情况下完成此操作。你通常应该避免改变引用,除非在Clojure中100%是必需的。
改变原子/ agent / refs的函数名称可能不同,可能是因为它们的机制不同。参考是通过交易同步和协调的。代理是异步的。原子同步且不协调。它们都是“改变引用”,并且可能某种超级函数或宏可以将它们全部包含在一个名称中,但这会掩盖它们在引擎盖下做的截然不同的事实。完全解释差异可能超出了SO职位的解释范围,但http://clojure.org完全解释了差异的所有细微差别。