为了学习Clojure的基础知识,我正在编写一个Sudoku解决方案 checker 。我已将提供的解决方案存储在二维矢量中。
(def solution [[4 2 9 8 1 3 5 6 7]
[5 1 6 4 7 2 9 3 8]
[7 8 3 6 5 9 2 4 1]
[6 7 2 1 3 4 8 5 9]
[3 9 5 2 8 6 1 7 4]
[8 4 1 7 9 5 6 2 3]
[1 5 8 3 6 7 4 9 2]
[9 3 4 5 2 8 7 1 6]
[2 6 7 9 4 1 3 8 5]])
在Java中,我可以很容易地使用嵌套循环来分割水平线,垂直线和正方形,但我不知道如何在Clojure中处理它。
我的第一次尝试产生了这样的东西来获得横向的总和:
(def horizontals [])
(for [i solution]
(conj horizontals (reduce + i)))
打印出来的:
([45] [45] [45] [45] [45] [45] [45] [45] [45])
这是检查水平线的“正确”方法吗?或者在Clojure中有更好的方法吗?我如何检查垂直或正方形的总和?
答案 0 :(得分:4)
水平很容易。它实际上只是solutions
:你想要一个数字列表清单,这就是你已有的数据!
我无法想象为什么要总结一些东西,因为这与检查数独解决方案无关(这应该涉及检查唯一性)。但是,如果你这样做,你可以用(map #(reduce + %) solution)
对每个水平相加。
纵向涉及一个巧妙的技巧:你可以使用(apply map vector m)
转置"矩阵"嵌套向量。所以只需将解决方案旋转90度,然后检查其水平线!当然,如果你想,你可以用同样的方式添加它们,虽然我再也不知道为什么。
正方形更有趣,有几种方法可以解决它。我会使用get-in
和一系列坐标对,例如:
(defn squares [solution]
(for [y (range 3)
x (range 3)]
(for [y' (range 3)
x' (range 3)]
(get-in solution [(+ y' (* 3 y))
(+ x' (* 3 x))]))))
为了完整性:我确实说你应该检查唯一性而不是总结(毕竟,9 5s总和45,但这绝对不是一个好的解决方案!)。这是验证9个数字列表构成有效行/列/方块的一种方法:
(defn valid? [numbers]
(and (= 9 (count numbers))
(= (set (range 1 10))
(set numbers)))
答案 1 :(得分:1)
虽然这不是问题的算法部分的严格答案,但我认为值得指出的是,这段代码可能没有按照您的想法进行:
(def horizontals [])
(for [i solution]
(conj horizontals (reduce + i)))
您似乎希望以迭代方式评估for
,并且每次调用conj
以更新var horizontals
的内容,将每行的总和追加到矢量。这绝对是不在Clojure中的运作方式。
相反,空向量[]
是不可变的;在向量上调用conj
不会改变向量的内容 - 它返回一个全新的向量,它与原始向量相同但添加了新的元素。
同样,调用(conj horizontals (reduce + i))
不会更新horizontals
的当前值。它是一个函数调用,它取当前值horizontals
(向量)并返回一个新值(另一个不同的向量);这取决于你对这个结果做些什么。通常,一旦你def
变量,它在整个程序中保持不变,除非你明确地改变它。通过重新定义变量进行编程在Clojure中非常不受欢迎。
最后,for
不是Clojure中的循环结构,它是列表理解。基本上它所做的是创建一个惰性序列,该序列由内部表达式(conj
函数调用)的结果组成,包含您提供的一系列绑定([i solution]
部分)。在使用生成的延迟序列之前,表达式实际上不会执行 - 在您的情况下,通过在REPL中打印结果。