正确看待递归函数

简介: 实际编码过程中,我觉得尽量少用,而是使用for或while去替代它,这样会让你少走很多弯路。​

什么是递归函数

我们都知道基本上的编程语言都支持在一个函数中调用其他的函数。如果这个函数在内部调用它自己,那么我们就称这个函数为递归函数。

递归函数的作用

  • 可以执行for或while语句相同的任务
  • 有些情况可以少写代码,让代码看起来更简练
    举一个例子,数学中我们有学习过求一个正整数的阶乘。阶乘是基斯顿·卡曼(Christian Kramp,1760~1826)于 1808 年发明的运算符号,是数学术语。一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积。我们使用PHP语言用两种方法实现阶乘的运算,代码如下:
<?php
​
//使用递归的方式
function factorial_1($n){
  return $n == 1 ? 1 : $n * factorial($n - 1);
}
//使用while的方式
function factorial_2($n){
  $res = 1;
  while ($n > 1) {
    $res = $res * $n--;
  }
  return $res;
}
$n = 5;
echo "使用阶乘的方式:" . factorial_1($n) . "<br>";
echo "使用while的方式: " . factorial_2($n) . "<br>";

打开浏览器运行该脚本,可以得到相同的结果。观察两个函数的实现,不难发现使用阶乘的方式,代码量看起来似乎是少一些,但是也没有特别明显。

递归函数的弊端

  • 性能低于for和while,同时会占用更高的内存
  • 如果不设置递归的边界,那么就会造成死循环,直接将服务器跑崩
  • 虽然可以替代for和while的实现,但是有些情况使用递归会显得很难理解。这边举个例子(当时也是困扰苟哥我许久啊),代码实现如下:
<?php
//用两个方法实现将一个字符串逆置
​
//递归实现
function reverse_r($str) { 
  if (strlen($str)>0) {
    reverse_r(substr($str, 1)); 
  }
  echo substr($str, 0, 1);
  return; 
}
​
//for循环实现
function reverse_i($str) {
  for ($i=1; $i<=strlen($str); $i++) {
    echo substr($str, -$i, 1); 
  }
  return; 
}
​
reverse_r('Hello'); 
echo '<br />'; 
reverse_i('Hello');

虽然两个方法得到的结果是一样的,但是明显reverse_i这个方式是更好理解的,直接从字符串的最后一个位置开始逐个打印字符。而分析reverse_r这个方法,你会发现不是很好理解,如果你之前不知道一个事实——“函数内,当遇到子函数后并不是停止运行函数后面的代码,而是保存现场,去执行子函数,执行完恢复现场接着执行”,那么我猜你一定很难理解程序是怎么实现字符串逆置的。什么意思呢?就是reverse_i脚本的第9行那句代码会执行n次(n表示递归次数),但是每次执行的顺序刚好是和函数的循环顺序相反的,我们借助下图来理解reverse_i是如何被执行的:
_
上图中,右侧带数字的向下箭头表示程序实际运行的代码顺序。被不同颜色的线连接的表示一个完整的函数体(因为我们刚刚说了遇到子函数后并不是停止运行函数后面的代码,而是保存现场,去执行子函数,执行完恢复现场接着执行),因此reverse_r('hello')虽然是最先被执行的,但是它的后一句代码echo substr('hello', 0, 1) 却是最后才被执行的。最后得到的打印效果也就实现了将字符串逆置,但是用递归这个方式确实不容易被理解啊。

应该使用递归函数吗

从知识点全面性的角度,我觉得可以也应该去学习递归思想。但是在实际编码过程中,我觉得尽量少用,而是使用for或while去替代它,这样会让你少走很多弯路。

目录
相关文章
|
19天前
无法连接共享打印机 0*0000709 错误?win7连接win11的打印机709报错?
无法连接共享打印机 0*0000709 错误?win7连接win11的打印机709报错?
328 1
|
关系型数据库 Java Go
解决 MyBatis-Plus + PostgreSQL 中的 org.postgresql.util.PSQLException 异常
解决 MyBatis-Plus + PostgreSQL 中的 org.postgresql.util.PSQLException 异常
1821 0
|
XML 安全 程序员
了解编程语言中的类型安全
【7月更文挑战第15天】本文介绍类型安全与效率在语言设计中至关重要。安全性需求各异,从HIPAA到PCI合规,选择最佳技术栈要考虑多方面,包括微服务架构中的语言多样性,以确保每个服务都能高效且安全地执行其特定任务。
313 14
了解编程语言中的类型安全
|
前端开发 JavaScript
screenshot-to-code
screenshot-to-code
367 0
|
Java 测试技术 开发者
Java线程池ThreadPoolExcutor源码解读详解09-4种拒绝策略
本文介绍了线程池的四种拒绝策略:AbortPolicy、DiscardPolicy、DiscardOldestPolicy和CallerRunsPolicy,并通过代码示例展示了它们在任务过多时的不同处理方式。AbortPolicy会抛出异常并停止主线程;DiscardPolicy会默默丢弃新任务;DiscardOldestPolicy会抛弃队列中最旧的任务来接纳新任务;而CallerRunsPolicy则是由调用者线程执行被拒绝的任务,以减缓新任务的提交速度。这四种策略适用于不同的场景,开发者可以根据需求选择合适的策略。
1383 5
|
机器学习/深度学习 运维
深度学习实现自编码器Autoencoder神经网络异常检测心电图ECG时间序列
深度学习实现自编码器Autoencoder神经网络异常检测心电图ECG时间序列
|
弹性计算 关系型数据库 MySQL
navicat通过ssh连接方式远程mysql(远程mysql3306端口关闭或者只允许localhost链接状态)...
navicat通过ssh连接方式远程mysql(远程mysql3306端口关闭或者只允许localhost链接状态)...
787 1
navicat通过ssh连接方式远程mysql(远程mysql3306端口关闭或者只允许localhost链接状态)...
|
Linux 编译器 开发工具
Linux: 远程登陆Linux之 X-Shell和Xftp的下载和使用+vi和vim编译器模式转换及快捷键
Linux: 远程登陆Linux之 X-Shell和Xftp的下载和使用+vi和vim编译器模式转换及快捷键
275 0
Linux: 远程登陆Linux之 X-Shell和Xftp的下载和使用+vi和vim编译器模式转换及快捷键
|
SQL Oracle 关系型数据库