【问题标题】:Recursion producing extra empty lists in Clojure递归在 Clojure 中产生额外的空列表
【发布时间】:2016-11-08 07:05:56
【问题描述】:

这个问题是关于本课程的练习:http://iloveponies.github.io/120-hour-epic-sax-marathon/sudoku.html

我对函数solve 的实现返回了已解决的数独板,但除了它周围还有很多空列表。我的所有其他功能都可以正常工作,并且不会返回任何空列表。我不明白为什么会这样。

以下是相关代码:

(defn solve [board]
  (if-let [point (find-empty-point board)]
    (let [valid-values (valid-values-for board point)]
      (for [value valid-values]
        (solve (set-value-at board point value))))
    (if (valid-solution? board)
      board)))

(def sudoku-board
  (board [[5 3 0 0 7 0 0 0 0]
          [6 0 0 1 9 5 0 0 0]
          [0 9 8 0 0 0 0 6 0]
          [8 0 0 0 6 0 0 0 3]
          [4 0 0 8 0 3 0 0 1]
          [7 0 0 0 2 0 0 0 6]
          [0 6 0 0 0 0 2 8 0]
          [0 0 0 4 1 9 0 0 5]
          [0 0 0 0 8 0 0 7 9]]))

输出:

(solve sudoku-board)
(((((()) ())
((((((() (())) (())) ((() (())) ((()))) ((()) ((()))))))
(() ((((() (())) (())) ((() (())) ((()))) ((()) ((()))))))
(((((() (())) (())) ((() (())) ((()))) ((()) ((())))))))
((()) (())))
(((((((() (())) (())) ((() (())) ((()))) ((()) ((()))))))
(() ((((() (())) (())) ((() (())) ((()))) ((()) ((()))))))
(((((() (())) (())) ((() (())) ((()))) ((()) ((())))))))

...

(((((((((((((((([[5 3 4 6 7 8 9 1 2]
                                                    [6 7 2 1 9 5 3 4 8]
                                                    [1 9 8 3 4 2 5 6 7]
                                                    [8 5 9 7 6 1 4 2 3]
                                                    [4 2 6 8 5 3 7 9 1]
                                                    [7 1 3 9 2 4 8 5 6]
                                                    [9 6 1 5 3 7 2 8 4]
                                                    [2 8 7 4 1 9 6 3 5]
                                                    [3 4 5 2 8 6 1 7 9]]))))))))))

...

((((((((()) ((((((()) (()))))) (((((((()))) ((())))))))) ((((((() ()) (())))) ((((() ()) (()))))) (())))
                  (((((((() ())))) ((((() ()))))) (()) (((((() ())))) ((((() ()))))))
                   ((((())) ()) ((() (())) (()) (())))
                   (((() (((())))) (()) (())) (((((())))) ())))
                  (((()) ((((((()) (()))))) (((((((()))) ((())))))))) (((((((((((((()))))))))))) ())))))
                ()))))))
          ())))))
     ())))))

【问题讨论】:

    标签: recursion clojure


    【解决方案1】:

    正如 coredump 所说,您需要更加小心地组合递归调用的结果。特别是,请注意您的“返回类型”不一致:在if-letelse 分支中,您返回一个解决方案,但在then 分支中,您返回由@987654324 生成的解决方案列表@。

    为了能够有意义地调用您的函数,它应该返回一个一致的类型:解决方案列表。这是一个简单的更改,将返回值更改为始终为列表,并确保连接所有子列表以避免引入额外的嵌套级别:

    (defn solve [board]
      (if-let [point (find-empty-point board)]
        (let [valid-values (valid-values-for board point)]
          (for [value valid-values
                solution (solve (set-value-at board point value))]
            solution))
        (if (valid-solution? board)
          [board])))
    

    【讨论】:

      【解决方案2】:

      for 构造将列表解析主体中计算的每个值累积为惰性序列。当您递归调用求解时,您正在计算嵌套序列中的此类序列等。

      您可能应该附加这些列表以使结果变平。

      就个人而言,但我不确定 Clojure 是多么地道,我只会接受一个回调函数fn 并执行一个doseq 而不是for,这样对于每个解决方案板,您只需要调用fn 在那个板上(你实现了一个生成器)。由调用者对解决方案做任何想做的事情。

      【讨论】:

      • for 已经是一个生成器。当存在使用惰性序列的简单解决方案时,使用 doseq 和回调将是非常糟糕的风格。
      • @amalloy 这就是我怀疑的原因。谢谢
      • @tuulik 你可能应该接受另一个答案
      猜你喜欢
      • 1970-01-01
      • 2012-10-25
      • 2011-12-24
      • 1970-01-01
      • 1970-01-01
      • 2023-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多