【发布时间】:2014-03-07 07:56:09
【问题描述】:
Clojure 初学者在这里。这是我试图理解的一些代码,来自http://iloveponies.github.io/120-hour-epic-sax-marathon/sudoku.html(一页相当不错的 Clojure 入门课程):
Subset sum is a classic problem. Here’s how it goes. You are given:
a set of numbers, like #{1 2 10 5 7}
and a number, say 23
and you want to know if there is some subset of the original set that sums up to the target.
We’re going to solve this by brute force using a backtracking search.
Here’s one way to implement it:
(defn sum [a-seq]
(reduce + a-seq))
(defn subset-sum-helper [a-set current-set target]
(if (= (sum current-set) target)
[current-set]
(let [remaining (clojure.set/difference a-set current-set)]
(for [elem remaining
solution (subset-sum-helper a-set
(conj current-set elem)
target)]
solution))))
(defn subset-sum [a-set target]
(subset-sum-helper a-set #{} target))
So the main thing happens inside subset-sum-helper. First of all, always check if we have found
a valid solution. Here it’s checked with
(if (= (sum current-set) target)
[current-set]
If we have found a valid solution, return it in a vector (We’ll see soon why in a vector). Okay,
so if we’re not done yet, what are our options? Well, we need to try adding some element of
a-set into current-set and try again. What are the possible elements for this? They are those
that are not yet in current-set. Those are bound to the name remaining here:
(let [remaining (clojure.set/difference a-set current-set)]
What’s left is to actually try calling subset-sum-helper with each new set obtainable
in this way:
(for [elem remaining
solution (subset-sum-helper a-set
(conj current-set elem)
target)]
solution))))
Here first elem gets bound to the elements of remaining one at a time. For each elem,
solution gets bound to each element of the recursive call
solution (subset-sum-helper a-set
(conj current-set elem)
target)]
And this is the reason we returned a vector in the base case, so that we can use for
in this way.
果然,(subset-sum #{1 2 3 4} 4) 返回(#{1 3} #{1 3} #{4})。
但是为什么subset-sum-helper的第3行必须返回[current-set]?这不会返回([#{1 3}] [#{1 3}] [#{4}]) 的最终答案吗?
我尝试删除第 3 行中的括号,使函数开始如下:
(defn subset-sum-helper [a-set current-set target]
(if (= (sum current-set) target)
current-set
(let ...
现在(subset-sum #{1 2 3 4} 4) 返回(1 3 1 3 4),这使得let 看起来不是累积三个集合#{1 3}、#{1 3} 和#{4},而只是“裸”数字,给(1 3 1 3 4)。
所以subset-sum-helper 在递归计算中使用列表理解for,我不明白发生了什么。当我尝试可视化这个递归计算时,我发现自己在问,“那么当
(subset-sum-helper a-set
(conj current-set elem)
target)
不返回答案,因为在给定起点的情况下没有答案?”(我最好的猜测是它返回 [] 或类似的东西。)我不明白教程作者写的时候是什么意思, “这就是我们在基本情况下返回向量的原因,以便我们可以通过这种方式使用for。”
如果您能给我任何帮助,我将不胜感激。谢谢!
【问题讨论】:
-
我现在已经更新了我的答案
标签: for-loop recursion clojure list-comprehension