递归有什么用吗?
Is there any use for recursions?
我最近了解到尾递归是一种递归方法,当您给它的数字太大而无法使用时,它不会崩溃。我意识到我可以轻松地将尾递归重写为 while 循环,并让它做基本上完全相同的事情,这让我想知道——当你可以用普通循环做所有事情时,递归还有什么用吗?
是的,递归代码看起来更小也更容易理解,但它也有可能完全崩溃,而简单的循环在执行相同任务时不会崩溃。
我以Haskell language为例,它是纯功能:
Every function in Haskell is a function in the mathematical sense
(i.e., "pure"). Even side-effecting IO operations are but a
description of what to do, produced by pure code. There are no
statements or instructions, only expressions which cannot mutate
variables (local or global) nor access state like time or random
numbers.
因此,在 haskell 中,递归函数是尾递归的,如果
递归调用的最终结果是函数本身。如果
递归调用的结果必须进一步处理(例如,通过添加
1 到它,或者将另一个元素放到它的开头),它是
不是尾递归。 (see here)
另一方面,在许多编程语言中,调用函数使用堆栈 space,因此尾递归的函数可以建立大量对其自身的调用堆栈,从而浪费内存。由于在尾调用中,包含函数即将 return,它的环境实际上可以被丢弃,并且可以在不创建新堆栈帧的情况下进入递归调用。这个技巧称为尾调用消除或尾调用优化,并允许尾递归函数无限期地重复出现。
我发布这个问题已经有很长一段时间了,我对这个话题的看法也发生了变化。原因如下:
我学会了 Haskell,它是一种修复递归所有坏处的语言 - 递归定义和算法变成了正常的循环算法,大多数时候你甚至不直接使用递归而是使用map
、fold
、filter
或它们的组合。去除所有不好的东西后,函数式编程好的一面开始显现出来——一切都更接近它的数学定义,而不是被笨拙的循环和变量所掩盖。
对于正在努力理解为什么递归很棒的其他人,请去学习 Haskell。它还有很多其他非常有趣的特性,比如惰性(值只在被请求时才被评估)、静态(变量永远不能被修改)、纯(函数除了接受输入和 return 输出之外不能做任何事情,所以没有打印到控制台),具有非常富有表现力的类型系统的强类型,充满了令人兴奋的抽象,如 Functor、Monad、State 等等。我几乎可以说它改变了生活。
我最近了解到尾递归是一种递归方法,当您给它的数字太大而无法使用时,它不会崩溃。我意识到我可以轻松地将尾递归重写为 while 循环,并让它做基本上完全相同的事情,这让我想知道——当你可以用普通循环做所有事情时,递归还有什么用吗?
是的,递归代码看起来更小也更容易理解,但它也有可能完全崩溃,而简单的循环在执行相同任务时不会崩溃。
我以Haskell language为例,它是纯功能:
Every function in Haskell is a function in the mathematical sense (i.e., "pure"). Even side-effecting IO operations are but a description of what to do, produced by pure code. There are no statements or instructions, only expressions which cannot mutate variables (local or global) nor access state like time or random numbers.
因此,在 haskell 中,递归函数是尾递归的,如果 递归调用的最终结果是函数本身。如果 递归调用的结果必须进一步处理(例如,通过添加 1 到它,或者将另一个元素放到它的开头),它是 不是尾递归。 (see here)
另一方面,在许多编程语言中,调用函数使用堆栈 space,因此尾递归的函数可以建立大量对其自身的调用堆栈,从而浪费内存。由于在尾调用中,包含函数即将 return,它的环境实际上可以被丢弃,并且可以在不创建新堆栈帧的情况下进入递归调用。这个技巧称为尾调用消除或尾调用优化,并允许尾递归函数无限期地重复出现。
我发布这个问题已经有很长一段时间了,我对这个话题的看法也发生了变化。原因如下:
我学会了 Haskell,它是一种修复递归所有坏处的语言 - 递归定义和算法变成了正常的循环算法,大多数时候你甚至不直接使用递归而是使用map
、fold
、filter
或它们的组合。去除所有不好的东西后,函数式编程好的一面开始显现出来——一切都更接近它的数学定义,而不是被笨拙的循环和变量所掩盖。
对于正在努力理解为什么递归很棒的其他人,请去学习 Haskell。它还有很多其他非常有趣的特性,比如惰性(值只在被请求时才被评估)、静态(变量永远不能被修改)、纯(函数除了接受输入和 return 输出之外不能做任何事情,所以没有打印到控制台),具有非常富有表现力的类型系统的强类型,充满了令人兴奋的抽象,如 Functor、Monad、State 等等。我几乎可以说它改变了生活。