在Clojure中,如何检查数独解决方案是否有效?
为了学习Clojure的基础知识,我正在编写一个数独解决方案检查器。我已将提供的解决方案存储在二维向量中在Clojure中,如何检查数独解决方案是否有效?,clojure,sudoku,Clojure,Sudoku,为了学习Clojure的基础知识,我正在编写一个数独解决方案检查器。我已将提供的解决方案存储在二维向量中 (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] [
(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有更好的方法?如何检查垂直线或正方形的总和?水平线很容易。从字面上看,这只是
解决方案
:您需要一个数字列表,而这正是您已经拥有的
我无法想象为什么要求和,因为这与检查数独解决方案无关(应该包括检查唯一性)。但是,如果您这样做了,您可以使用(map#(reduce+%)解决方案)
对每个水平面求和
垂直方向包含一个巧妙的技巧:您可以使用(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)))
水平面很容易。从字面上看,这只是
解决方案
:您需要一个数字列表,而这正是您已经拥有的
我无法想象为什么要求和,因为这与检查数独解决方案无关(应该包括检查唯一性)。但是,如果您这样做了,您可以使用(map#(reduce+%)解决方案)
对每个水平面求和
垂直方向包含一个巧妙的技巧:您可以使用(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)))
虽然这并不是严格地回答您问题中的算法部分,但我认为值得指出的是,这段代码可能没有做您认为它正在做的事情:
(def horizontals [])
(for [i solution]
(conj horizontals (reduce + i)))
您似乎期望以迭代的方式对进行评估,并且每次调用conj
更新var水平
的内容,将每行的总和追加到向量。这显然不是Clojure的运作方式
相反,空向量[]
是不可变的;对向量调用conj
不会改变向量的内容——它返回一个与原始向量相同但添加了新元素的全新向量
类似地,调用(conj horizontals(reduce+i))
不会更新horizontals
的当前值。这是一个函数调用,它获取当前值horizontals
(一个向量),并返回一个新值(另一个不同的向量);这取决于你对结果做些什么。一般来说,一旦您def
变量,它在整个程序中保持不变,除非您明确更改它。在Clojure中,通过重新定义变量进行编程是非常不受欢迎的
最后,for
在Clojure中不是循环结构,而是列表理解。本质上,它所做的是创建一个惰性序列,该序列由内部表达式的结果(调用conj
函数)和您提供的绑定序列(部分[i解决方案]
组成)。在使用生成的惰性序列之前,表达式实际上不会执行——在您的例子中,通过在REPL中打印结果。虽然这并不是对问题的算法部分的严格回答,但我认为值得指出的是,这段代码可能没有做您认为它正在做的事情:
(def horizontals [])
(for [i solution]
(conj horizontals (reduce + i)))
您似乎期望以迭代的方式对进行评估,并且每次调用conj
更新var水平
的内容,将每行的总和追加到向量。这显然不是Clojure的运作方式
相反,空向量[]
是不可变的;对向量调用conj
不会改变向量的内容——它返回一个与原始向量相同但添加了新元素的全新向量
类似地,调用(conj horizontals(reduce+i))
不会更新horizontals
的当前值。这是一个函数调用,它获取当前值horizontals
(一个向量),并返回一个新值(另一个不同的向量);这取决于你对结果做些什么。一般来说,一旦您def
变量,它在整个程序中保持不变,除非您明确更改它。在Clojure中,通过重新定义变量进行编程是非常不受欢迎的
最后,for
在Clojure中不是循环结构,而是列表理解。本质上,它所做的是创建一个由内部表达式的结果组成的惰性序列(代码