目录
当前位置: 首页 > 文档资料 > Clojure 入门教程 >

14 递归

优质
小牛编辑
123浏览
2023-12-01

递归

递归发生在一个函数直接或者间接调用自己的时候。一般来说递归的退出条件有检查一个集合是否为空,或者一个状态变量是否变成了某个特定的值(比如0)。这一种情况一般利用连续调用集合里面的 next 函数来实现。后一种情况一般是利用 dec 函数来递减某一个变量来实现。

如果递归的层次太深的话,那么可能会产生内存不足的情况。所以一些编程语言利用 “ tail call optimization ” (TCO)的技术来解决这个问题。但是目前Java和Clojure都不支持这个技术。在Clojure里面避免这个问题的一个办法是使用special form: looprecur 。另一个方法是使用 trampoline 函数。

loop / recur 组合把一个看似递归的调用变成一个迭代 — 迭代不需要占用栈空间。 loop special form 跟 let special form 类似的地方是它们都会建立一个本地binding,但是同时它也建立一个递归点, 而这个递归点就是recur的参数里面的那个函数。 loop 给这些binding一个初始值。对 recur 的调用使得程序的控制权返回给 loop 并且给那些本地binding赋了新的值。给recur传递的参数一定要和loop所创建的binding的个数一样。同样recur只能出现在loop这个special form的最后一行。

(defn factorial-1 [number]
  "computes the factorial of a positive integer
   in a way that doesn't consume stack space"
  (loop [n number factorial 1]
    (if (zero? n)
      factorial
      (recur (dec n) (* factorial n)))))

(println (time (factorial-1 5))) ; -> "Elapsed time: 0.071 msecs"\n120

defn 宏跟 loop special form一样也会建立一个递归点。 recur special form 也可以被用在一个函数的最后一句用来把控制权返回到函数的第一句并以新的参数重新执行。

另外一种实现 factorial 函数的方法是使用 reduce 函数。这个我们在 “集合” 那一节就已经介绍过了。它支持一种更加“函数”的方式来做这个事情。不过不幸的是,在这种情况下,它的效率要低一点。注意一下 range 函数返回一个数字的范围, 这个范围包括它的左边界,但是不包括它的右边界。

(defn factorial-2 [number] (reduce * (range 2 (inc number))))

(println (time (factorial-2 5))) ; -> "Elapsed time: 0.335 msecs"\n120

你可以把上面的 reduce 换成 apply, 可以得到同样的结果, 但是apply要更慢一点。这也说明了我们要熟悉每个方法的特点的重要性,以在各个场合使用合适的函数。

recur 不支持那种一个函数调用另外一个函数,然后那个函数再回调这个函数的这种递归。但是我们没有提到的 [trampoline](http://clojure.github.com/clojure/clojure.core-api.html) 函数是支持的。

最后更新:

类似资料

  • 我对函数式编程很陌生,尤其是下面使用的Scheme。我正在尝试使以下函数是递归的,尾递归的。基本上,该函数的作用是对两个字符串的对齐方式进行评分。当给定两个字符串作为输入时,它会比较每个“列”字符,并根据在称为 scorer 的函数中实现的评分方案(由下面的代码中的函数调用)来累积该对齐的分数。 我有一个想法,用一个帮助函数来累积分数,但我不太确定如何去做,因此我该如何让下面的函数尾递归呢?

  • 5.2. 递归 函数可以是递归的,这意味着函数可以直接或间接的调用自身。对许多问题而言,递归是一种强有力的技术,例如处理递归的数据结构。在4.4节,我们通过遍历二叉树来实现简单的插入排序,在本章节,我们再次使用它来处理HTML文件。 下文的示例代码使用了非标准包 golang.org/x/net/html ,解析HTML。golang.org/x/... 目录下存储了一些由Go团队设计、维护,对网

  • 递归允许函数调用自身。 固定的代码步骤一次又一次地执行新值。 我们还必须设置标准来决定递归调用何时结束。 在下面的例子中,我们看到了二进制搜索的递归方法。 我们采用排序列表并将其索引范围作为递归函数的输入。 使用递归进行二进制搜索 我们使用python实现二进制搜索算法,如下所示。 我们使用一个有序的项目列表,并设计一个递归函数,以带有开始和结束索引作为输入的列表。 然后二进制搜索函数调用自己直到

  • 递归过程就是自称过程。 有两种递归:直接和间接。 在直接递归中,过程调用自身并在间接递归中,第一个过程调用第二个过程,该过程又调用第一个过程。 可以在许多数学算法中观察到递归。 例如,考虑计算数字的阶乘的情况。 一个数的因子由等式给出 - Fact (n) = n * fact (n-1) for n > 0 例如:5的阶乘是1 x 2 x 3 x 4 x 5 = 5 x阶乘4,这可以是显示递归

  • 我们在前面的主题中看到了recur语句,而'for'循环有点像循环, recur是Clojure中的一个真正的循环。 如果你有编程背景,你可能听说过尾递归,这是函数式语言的一个主要特性。 这种复现特殊形式是实现尾递归的形式。 正如单词“tail recursion”所示,必须在尾部位置调用recur。 换句话说,复发必须是最后评估的东西。 最简单的recur语句示例在'for'循环中使用。 在以下

  • 递归是以自相似的方式重复项目的过程。 在编程语言中,如果程序允许您在同一函数内调用函数,则称其为函数的递归调用。 void recursion() { recursion(); /* function calls itself */ } int main() { recursion(); } C编程语言支持递归,即调用自身的函数。 但是在使用递归时,程序员需要小心地从函数中定义退出条

开发工具

我的快递