Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 学习递归读写_Recursion_Clojure - Fatal编程技术网

Recursion 学习递归读写

Recursion 学习递归读写,recursion,clojure,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))))) 我相信作为一个有经验的

我对Clojure很陌生,以前在其他语言中也见过这种模式。我想我确实知道递归是如何工作的,但我不知道如何阅读和思考,这样你就可以编写这样的代码。下面是一个简单的例子:

(defn remove-dups [str]
  (when-let [[fst & rst] (seq str)]
    (if (= fst (first rst))
      (remove-dups rst)
      (cons fst (remove-dups rst)))))

我相信作为一个有经验的法律专家,这看起来并不难。然而,我仍然不知道如何阅读这样的代码。也许有一种模式你必须学会理解?他们说Lisp和Clojure是“由内而外”阅读的,但我找不到一种快速的方法来解析这样的代码。也许有一种方法可以通过打印出步骤来“展开”递归模式?请有人教我用分而治之的方式思考。

我怀疑这是许多想接近Clojure的人都在问自己的问题,而且大多数人都不敢问,所以谢谢你的帮助。这也是一个非常个人的任务,让你的大脑围绕递归。我是根据经验说的。回到大学时,我是介绍递归的课程的助教(五年),我花了五年时间试图找出“方法”,向人们“展示”如何像这样展开代码片段

在这段时间里,我发展出了这些似乎在所有情况下都适用的想法:

  • 第一次曝光时,没有人能读懂这个密码
  • 一旦他们“明白了”,他们就无法用一种有用的方式向那些还没有“明白”的人解释
  • “得到它”所需的时间完全是随机的,与课程其他方面的成功没有任何关系(除非他们退课)
所以我的结论是,学习阅读这些代码会以某种方式改变你的大脑,而这种改变需要随机的时间。每个人的学习方式都不一样,结果都是一样的

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

  • 使用调试器观察它(如果需要,观察数小时):在Clojure中,这将设置Leiningen+emacs+clj重构并向函数添加跟踪。然后运行它并重复地通过它
  • 在纸上绘制堆栈(从页面底部开始),并一直运行许多示例。即使是在声称自己理解了这一点之后,那些一路走来的人做得更好
  • 有些人确实是通过几个小时专注地盯着代码来了解这一点的。作为一名教育工作者,除了不打断他们(老师经常打断学习),我不知道如何帮助他们
  • 有些人走出教室,去喝酒,然后带着理解回来。再一次,我不知道他们做了什么,尽管我怀疑他们比其他团体更有趣
耐心一点,学习真正阅读这个例子将改善你作为程序员生活的方方面面,在我个人看来,这个例子是完全好的,非常适合这个目的

我认为接触递归有助于理解递归。一旦你对这些数字的校样感到满意,试着阅读一些基于树/图的校样。数学归纳法通常从n到n+1。递归通常走另一条路——你有一个大小为n的问题,你把它简化为大小为n-1的问题。最终,您会遇到一个大小为1或0的问题,而这个问题通常很难解决

通常用于序列的分而治之策略是将序列分为头部(第一项)和尾部(可能是空的)(其余项)。这在上面的代码示例中通过行
[fst&rst](seq str)
完成。这使用了一个名为destructuring的Clojure特性。它相当于以下内容

(让[fst(第一个str)
rst(剩余str)])

这是处理序列的大多数递归函数中的常见习惯用法。一旦你划分了序列,你通常想以某种方式把它重新组合在一起(也许改变一些元素,删除一些元素,等等)。这样做的方式是使用
cons
一次构建一个元素序列

现在我们来看一下
删除重复项的细节:如果序列的头部与尾部的第一个元素相等,那么我们有两个连续的相等元素,函数的语义要求我们删除其中一个。我们去掉第一个(尾部),通过递归调用
(remove dups rst)
继续处理序列的其余部分。如果它们不相同,那么我们需要将头部保留在结果中,因此我们将其限制为
(remove dup rst)
(将在递归的下一步中计算)

简化版如下

(defn删除DUP[str]
(如果(空?str)
无
(让[fst(第一街)
rst(其余str)]
(如果(=fst(第一次rst))
(重新设置删除DUP)
(cons fst(删除DUP rst()(())))


进一步改进这一点的一种方法是在第3行返回一个空字符串“”,而不是nil。

旁白:这不是好代码,因为它会消耗堆栈,因此如果需要太多的递归步骤,就会崩溃。如果
loop
/
recur
trampoline
不能使用(这里不能使用loop/recur,因为它不在尾部位置),则可以使用不同的构造,即
for
。@CharlesDuffy我听到了。对不起,我找不到更好的例子了。如果我能写出更好的Clojure代码,我一开始就不会问这个问题。我认为,虽然这段代码完美地说明了我的斗争,斗争是真实的;[我听到了;不幸的是,我没有什么好的建议。我自己的方法是长时间的重复曝光,直到事情发生,但这并不意味着我知道如何让这一过程对其他人来说更容易。不过,是的,一个人可以“打开”不过,虽然我可以用白板来教,但用文本就不那么容易了。也许可以讨论一下在迭代之间转换的过程