递归
- 递归是一种函数调用自身的机制
- 递归必须要有边界条件,也就是递归出口(退出递归)
- 递归前进段和递归返回段,也就是最后得到的值
- 当边界条件不满足时,递归前进;当边界条件(递归出口)满足时,递归返回
递归就是函数自己调用自身
递归计算阶乘
输出结果
很明显的可以看到计算6!时,是6*5*4*3*2*1
,这就实现了递归。
例子: 目录的递归遍历
function loop_dir($path)
{ $dh = opendir($path); while($file=readdir($dh) !== false) { $current_file = $path.DIRECTORY_SEPARATOR.$file; if($file == '.' || $file == '..') { continue; } elseif (is_dir($current_file)) { echo 'Directory '.$current_file.':<br>'; loop_dir($current_file); } else { echo 'File in Directory '.$path.': '.$file.'<br>'; } } closedir(); //不加参数,关闭的是打开的最近打开的句柄,这样就保证函数结束时,关闭所有的句柄
}
利用递归函数处理多维数组
将多维数组中空的子数组删除
/**去除多维数组中空子数组* @param $arrayInput* @return mixed*/static function removeEmptyArray(&$arrayInput){foreach ($arrayInput as $sInputKey => $arrInputValue) {if (empty($arrInputValue)) {unset($arrayInput[$sInputKey]);} elseif (is_array($arrInputValue)) {self::removeEmptyArray($arrInputValue);} else {break;}}return $arrayInput;}
通过筛选串数组([’*.A.B’, ‘C’])去除多维数组中多余的字段
/*** 取得当前深度的筛选串* @param string $sField* @param int $nDepth 当前遍历数组深度* @return string*/static function getFields($sField, $nDepth = 1){$arrParts = explode('.', $sField, $nDepth + 1);if (count($arrParts) > 1 && count($arrParts) > $nDepth) {array_pop($arrParts);}$sFieldsOfTheDepth = implode('.', $arrParts);return $sFieldsOfTheDepth;}/*** 取得字符串的总深度* @param string $sPath* @return int*/static function getTotalDepth($sPath){$nFieldTotalDepth = substr_count($sPath, '.') + 1;return $nFieldTotalDepth;}/**去除多维数组中空子数组* @param $arrayInput* @return mixed*/static function removeEmptyArray(&$arrayInput){foreach ($arrayInput as $sInputKey => $arrInputValue) {if (empty($arrInputValue)) {unset($arrayInput[$sInputKey]);} elseif (is_array($arrInputValue)) {self::removeEmptyArray($arrInputValue);} else {break;}}return $arrayInput;}/**返回字符串的第一个单词 如: A.B.C 中的A* @param string $sPath* @return string*/static function handlePath($sPath){$nFirstPath = strpos($sPath, '.');if ($nFirstPath) {$sFirstPath = substr($sPath,0, $nFirstPath);} else {return $sPath;}return $sFirstPath;}/*** 递归比较子数组和筛选数组的路径串, 并筛选* @param array $arrSubArray 子数组* @param int $nDepth 实际数组下标深度* @param array $arrFields 筛选数组* @param string $sPartPath 源数组到子数组的实际路径串* @param string $sMethodSource 方法名 'exceptFields', 'onlyFields'* @return array 返回经过字段筛选后的数组*/static function pathFilter(&$arrSubArray, $nDepth, $arrFields, $sPartPath, $sMethodSource){//遍历筛选数组foreach ($arrFields as $sField) {//获取当前深度的筛选串路径$sTheDepthFields = self::getFields($sField, $nDepth);//获取筛选串的总长度, 用于在删除数组键值对时作为标记$sTheTotalDepthField = self::getTotalDepth($sField);//获取当前筛选串的深度, 用于在删除时和$sTheDepthFields作比较$nNowDepthFields = self::getTotalDepth($sTheDepthFields);//判断当前筛选串第一个字符是不是'*'$bIsWildCard = strpos($sField, '*') == 0 ? true : false;//遍历当前需要筛选的数组foreach ($arrSubArray as $sSubKey => $arrArray) {//拼接当前实际数组路径$sPartPath .= '.' . $sSubKey;$sPartPath = ltrim($sPartPath, ".");//获取当前实际数组路径的第一个单词$sFirstPartPath = self::handlePath($sPartPath);//获取当前实际数组路径的总深度, 用于在删除数组键值对时作为标记判断$sTheTotalDepthPartPath = self::getTotalDepth($sPartPath);//把当前筛选串第一个字母是'*'和当前实际数组路径的第一个单词进行替换if ($bIsWildCard) {$sTheDepthFields = str_replace('*', $sFirstPartPath, $sTheDepthFields);}//对当前的筛选串路径和实际数组路径进行比较$bJudge = $sTheDepthFields == $sPartPath ? true : false;switch ($sMethodSource) {case 'exceptFields':if ($bJudge && ($sTheTotalDepthPartPath == $sTheTotalDepthField)) {unset($arrSubArray[$sSubKey]);//将当前实际数组路径删除数组的键 例:A.B => A$sPartPath = substr($sPartPath, 0, strlen($sPartPath) - strlen($sSubKey) - 1 );continue 2;} elseif (is_array($arrArray)) {$nDepth++;$arrSubArray[$sSubKey] = self::pathFilter($arrArray, $nDepth, $arrFields, $sPartPath, $sMethodSource);}break;case 'onlyFields':if (!$bJudge && ($nNowDepthFields == $sTheTotalDepthPartPath)) {unset($arrSubArray[$sSubKey]);$sPartPath = substr($sPartPath, 0, strlen($sPartPath) - strlen($sSubKey) - 1 );continue 2;} elseif (is_array($arrArray)) {$nDepth++;$arrSubArray[$sSubKey] = self::pathFilter($arrArray, $nDepth, $arrFields, $sPartPath, $sMethodSource);//处理当前实际总的数组路径小于总的筛选串路径情况 例如: 筛选串 : *.META 当前总的数组路径 : ['K' => '1222']} elseif ($sTheTotalDepthField > $sTheTotalDepthPartPath) {unset($arrSubArray[$sSubKey]);}//这里对当前空数组进行删除操作self::removeEmptyArray($arrSubArray);break;}//针对于遍历完一个子数组, 将子数组第一个单词给还原为'*' | ' ' | '原单词'if (strlen($sPartPath) > strlen($sSubKey)){$sPartPath = substr($sPartPath, 0, strlen($sPartPath) - strlen($sSubKey) - 1 );} else {$sPartPath = '';}
//针对于遍历完一个子数组, 将筛选串第一个单词给还原为'*' | ' ' | '原单词'if ($bIsWildCard ) {$sFirstFields = self::handlePath($sTheDepthFields);$sTheDepthFields = str_replace($sFirstFields, '*', $sTheDepthFields);}}}return $arrSubArray;}/*** 筛选数组中的字段,仅保留指定的字段* @param array $arrArray 要筛选的源数组* @param string|array $arrFields 如:'*.meta.user_id'*/public static function onlyFields(&$arrArray, $arrFields){$arrFields = (array)$arrFields;self::pathFilter($arrArray, 1, $arrFields, '', 'onlyFields');self::removeEmptyArray($arrArray);}/*** 筛选数组中的字段,除了指定的字段,其它字段全部保留* @param array $arrArray 要筛选的源数组* @param string|array $arrFields 如:['*.meta.password', '*.deleted_at']*/public static function exceptFields(&$arrArray, $arrFields){$arrFields = (array)$arrFields;self::pathFilter($arrArray, 1, $arrFields, '', 'exceptFields');self::removeEmptyArray($arrArray);}}