我是Clojure的新手,之前我曾在其他语言中见过这种模式。我想我确实知道递归是如何工作的,但我不知道如何阅读以及如何思考方式,这允许你编写这样的代码。这是一个微不足道的例子:
(defn remove-dups [str]
(when-let [[fst & rst] (seq str)]
(if (= fst (first rst))
(remove-dups rst)
(cons fst (remove-dups rst)))))
我确信经验丰富的Clojurist这看起来并不困难。但是,我仍然不知道如何阅读这样的代码。也许你必须学习理解这种模式?他们说Lisp和Clojure从内到外阅读"但是我找不到一种快速的方法来精神上解析这样的代码。也许有一种解开"解开"的方法。打印出步骤的递归模式?请有人教我以分而治之的方式思考。
答案 0 :(得分:4)
我怀疑这是一个问题,许多想要接近Clojure的人都在问自己,而且大多数人都不敢问,所以感谢你们伸出援手。将你的大脑包裹在递归中也是一项非常个人化的工作。我从expierence说出来。回到大学我是一个讲授递归(五年)的课程的助教,我花了五年时间试图找出"方式"到"显示"人们如何解开像这样的代码片段。
在那段时间里,我发展了这些似乎在每种情况下都存在的想法:
所以我的结论是学习阅读这段代码会以某种方式改变你的大脑,并且这种改变需要一段随机的时间。每个人都以不同的方式学习它,每个人都在同一个地方。
以下是我为不同的人采取的一些方法:
要有耐心,学会真正阅读这个例子将改善你作为程序员的生活的方方面面,而且我个人认为这个例子完全没问题,非常适合这个目的。
答案 1 :(得分:1)
我认为接触Mathematical induction可以帮助理解递归。一旦您对这些数字证明感到满意,请尝试根据树木/图表阅读一些。数学归纳通常从n到n + 1。递归通常是另一种方式 - 你有一个大小为n的问题,你把它减少到大小为n-1的问题。最终你最终遇到了1或0的问题,这通常很容易解决。
通常用于序列的分治策略是将它们分为头部(第一项)和(可能是空的)尾部(其余项目)。这是在上面的代码示例中使用行[fst & rst] (seq str)
完成的。这使用称为解构的Clojure功能。它等同于以下
(let [fst (first str)
rst (rest str)])
这是处理序列的大多数递归函数中的常见习语。一旦你对序列进行了划分,你通常希望以某种方式将它重新组合在一起(可能会改变一些元素,丢弃一些元素等)。你这样做的方法是缺点使用cons
一次打破一个序列元素。
现在到remove-duplicates
的细节:如果序列的头部等于尾部的第一个元素,那么我们有两个相等的连续元素,函数的语义要求我们删除一个他们我们删除第一个(尾部)并继续处理序列的其余部分,通过递归调用(remove-dups rst)
。如果它们不相同,那么我们需要将结果保留在结果中,因此我们将其限制为(remove-dups rst)
(将在递归的下一步中计算)。
简化版如下。
(defn remove-dups [str]
(if (empty? str)
nil
(let [fst (first str)
rst (rest str)]
(if (= fst (first rst))
(remove-dups rst)
(cons fst (remove-dups rst))))))
进一步改善这种情况的一种方法是返回一个空字符串""而不是第3行的零。