学习递归阅读和写作

时间:2015-12-23 00:16:19

标签: recursion clojure

我是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从内到外阅读"但是我找不到一种快速的方法来精神上解析这样的代码。也许有一种解开"解开"的方法。打印出步骤的递归模式?请有人教我以分而治之的方式思考。

2 个答案:

答案 0 :(得分:4)

我怀疑这是一个问题,许多想要接近Clojure的人都在问自己,而且大多数人都不敢问,所以感谢你们伸出援手。将你的大脑包裹在递归中也是一项非常个人化的工作。我从expierence说出来。回到大学我是一个讲授递归(五年)的课程的助教,我花了五年时间试图找出"方式"到"显示"人们如何解开像这样的代码片段。

在那段时间里,我发展了这些似乎在每种情况下都存在的想法:

  • 第一次曝光时,没有人能阅读此代码
  • 一旦他们得到它"然后他们无法以有用的方式向那些尚未获得它的人解释它#34;。
  • 获得它所需的时间"完全 RANDOM 并且与课程的其他方面的成功无关(除非他们放弃了课程)

所以我的结论是学习阅读这段代码会以某种方式改变你的大脑,并且这种改变需要一段随机的时间。每个人都以不同的方式学习它,每个人都在同一个地方。

以下是我为不同的人采取的一些方法:

  • 使用调试器观察它(如果需要,可以持续数小时):在Clojure中,这将设置Leiningen + emacs + clj-refactor并在函数中添加跟踪。然后运行它并反复踩过它。
  • 在纸上绘制堆栈(从页面底部开始)并通过运行许多示例。即使在他们声称理解它之后一直走过的人也做得更好。
  • 有些人确实通过专注地盯着代码了解了几个小时。作为一名教育工作者,我不知道如何帮助解决这些问题,而不是打断他们(教师经常打断学习)
  • 有些人走出教室,喝酒,然后回来了解。再一次,不知道他们做了什么,虽然我怀疑他们比其他团体更有乐趣。

要有耐心,学会真正阅读这个例子将改善你作为程序员的生活的方方面面,而且我个人认为这个例子完全没问题,非常适合这个目的。

答案 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行的零。