如何从嵌套剂量中脱颖而出

fiz*_*bin 9 clojure

我有一个关于嵌套doseq循环的问题.在start函数中,一旦我找到答案,我将atom设置为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)))))))
Run Code Online (Sandbox Code Playgroud)

Bri*_*per 13

即使原子设置为true,你的版本也不会停止运行直到内部doseq完成(直到y> x).一旦内循环结束,它将终止外循环.当我运行它时它最终会终止.不知道你在看什么.

你不需要两个doseqs就可以做到这一点.一次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]
Run Code Online (Sandbox Code Playgroud)

(同样的情况也是如此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])))
Run Code Online (Sandbox Code Playgroud)

你的代码风格非常重要."遍历这些列表,然后测试值,然后打印一些内容,然后停止循环." 在Clojure中使用原子进行控制并不是非常惯用的.

一个更实用的方法是采用seq(五边形列表)并将其包装在将其转换为其他seq的函数中,直到获得一个能够提供所需内容的seq.首先,我使用for将此seqs的两个副本转换为一对seq,其中y <= x.然后我用filter这个seq变成一个过滤掉我们不关心的值的seq.

filter并且for是懒惰的,所以一旦找到first有效值,这将停止运行,如果你想要的话.这将返回您想要的两个数字,然后您可以减去它们.

(apply - (first answers))
Run Code Online (Sandbox Code Playgroud)

或者您可以进一步将该功能包装在另一个中map以计算您的差异.

(def answers2 (map #(apply - %) answers))
(first answers2)
Run Code Online (Sandbox Code Playgroud)

以这种方式在功能上编程有一些优点.seq被缓存(如果你像我一样按住头部),所以一旦计算出一个值,它就会记住它,然后你可以立即访问它.如果不重置原子,你的版本将无法再次运行,然后必须重新计算所有内容.使用我的版本,您(take 5 answers)可以获得前5个结果,或者根据结果映射以执行其他操作(如果需要).您可以doseq覆盖它并打印值.等等

我确信还有其他(可能更好)的方法可以在不使用原子的情况下做到这一点.你通常应该避免改变引用,除非它在Clojure中是100%必需的.

改变原子/代理/参考的函数名称可能不同,可能是因为它们的机制不同.参考是通过交易同步和协调的.代理是异步的.原子同步且不协调.它们都是"改变引用",并且可能某种超级函数或宏可以将它们全部包含在一个名称中,但这会掩盖它们在引擎盖下做的截然不同的事实.完全解释这些差异可能超出了SO职位的解释范围,但http://clojure.org充分解释了差异的所有细微差别.