PHP 递归函数

2025年3月15日 | 阅读 6 分钟

在计算机编程的语境中,尤其是当一个函数调用自身时,就会发生递归。该函数几乎总是有一个终止条件,称为基本情况,否则它会一直调用自身(或至少直到计算机内存耗尽)。

递归更适合某些问题,而迭代则更适合其他问题。迭代或递归通常都是解决给定问题的有效方法。PHP 也支持像 C/C++ 一样的递归函数调用。在这种情况下,我们在函数内部调用当前函数。这也被称为递归。

建议避免超过 200 级递归的递归函数调用,因为它可能会破坏堆栈并可能导致脚本终止。

如何使用 PHP 构建递归函数?

递归函数通常按以下方式运行:

  • 调用代码调用递归函数。
  • 函数执行任何必要的计算或处理。
  • 如果尚未达到基本情况,函数会调用自身以继续递归。因此会创建一个新的函数实例。
  • 如果遇到基本情况,函数只需将控制权返回给调用它的代码,从而停止递归。

伪代码中的基本递归函数如下所示:

代码

通过研究一些实际示例,可以更好地理解递归函数。让我们来看两个全面的 PHP 示例,它们演示了递归的作用:

  • 阶乘
  • 读取文件和文件夹树

示例 1:数字的阶乘

立即执行

输出

120

说明

定义函数

function factorial( $n ) {

首先定义函数 factorial(),并添加一个参数 $n 来存储要计算阶乘的数字。

设置基本情况

if ($n == 0)

return 1; /*终止条件*/

我们函数中首先定义了基本情况,即当提供的值 $n 等于 0 时。由于 0 的阶乘等于 1,如果满足此情况,我们只需返回 1,标志着递归的结束。外部调用代码或 factorial() 方法的另一个实例将从调用它的代码中获取此数字。

执行递归

return ($n * factorial ($n -1));

只要 $n 大于零,函数的最后一个代码块就会执行实际的递归。它在调用自身并将 $n-1 作为参数传递后,将结果乘以 $n。换句话说,将当前值乘以比当前值小 1 的整数的阶乘,以获得当前值的阶乘。

示例 2:读取文件和文件夹树

尽管前面提到的阶乘示例相当简单,但它有点——可以说——理论化。让我们来看一个非常有用的递归用法:读取硬盘目录结构并显示其中包含的文件和文件夹列表。

一个文件夹通常包含一个或多个文件和/或子文件夹。每个子文件夹可能包含额外的文件和子文件夹,依此类推。文件系统的树状结构使其非常适合递归。

  • 我们的文件夹读取功能必须这样运行:
  • 检查提供的文件夹的内容。
  • 显示文件夹中每个文件的名称。

如果文件夹中存在子文件夹,则显示子文件夹的名称,然后使用递归调用该方法来读取其内容,依此类推。

这是我们的完整脚本

立即执行

让我们检查 listDirectoryContents() 方法以了解其操作方式:

  • 启动函数

我们在创建方法 listDirectoryContents() 时,将要读取的文件夹路径包含在参数 $path 中。

  • 打开文件夹

使用 PHP 的 opendir() 方法打开文件夹进行读取后,我们创建一个名为 $filenames 的新数组,以存储我们在其中发现的任何文件和子文件夹的名称。

  • 检查文件夹的内容

然后,该方法使用 while 循环读取文件夹中的每个项目。当前文件夹的 '.' 和父文件夹的 '..' 条目被省略。如果该项目是文件夹,我们会在文件名末尾添加一个 '/',这可以通过使用 PHP is_dir() 函数来确定。除了告知用户这是一个文件夹外,我们稍后将在函数中查找此斜杠,以确定该项目是文件还是文件夹。最后,我们将每个项目的文件名添加到 $filenames 数组中。

由于我们不再需要目录句柄,我们在 while 循环结束后执行 PHP 函数 closedir() 来关闭目录。

  • 文件名应排序

PHP sort() 方法用于按字母顺序排列文件名数组以进行显示。

  • 处理任何子文件夹并显示文件名

最后,我们使用 foreach 循环遍历数组中的文件名。为了确定文件名是否对应于文件夹,我们既在 li 元素中显示名称,又查找尾随斜杠。如果是,则在递归执行 listDirectoryContents () 时发送文件夹的路径。通过这种方式,我们的函数迭代地遍历树中的每个文件夹,并在此过程中显示每个文件夹的内容。

输入

输出

<h1>Recursive Directory Listing</h1>
<h2>Contents of 'photos':</h2>
<ul>
  <li>folderA/
    <ul>
      <li>subimage1.jpg</li>
      <li>subimage2.png</li>
      <li>subfolderB/
        <ul>
          <li>deepimage1.jpg</li>
          <li>deepimage2.png</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>folderC/
    <ul>
      <li>anotherimage1.jpg</li>
      <li>anotherimage2.png</li>
    </ul>
  </li>
  <li>image1.jpg</li>
  <li>image2.png</li>
</ul>

php recursive function

创建递归 listDirectoryContents () 方法后,我们所要做的就是使用要读取的顶级文件夹的路径调用 listDirectoryContents (),该路径已存储在 $ dirPath 中。在这里,我设置了一个“photos”路径,它读取脚本所在文件夹内的“photos”子文件夹。

如果当前文件夹中有多个子文件夹。在这种情况下,readFolder() 方法可以在其 foreach 循环中多次调用自身,这与上面的 factorial() 示例不同,其中函数的每个版本只调用自身一次。函数的实例可以被认为形成一个树状结构,模仿硬盘的文件夹结构。

在此示例中,基本情况是当前文件夹没有子文件夹。此时,函数的实例停止调用自身。当函数执行完成后,它立即将控制权返回给调用它的函数。当控制权最终回到“树”的顶部时,脚本最终退出。


下一主题PHP 数组