實(shí)例助你理解php遞歸函數(shù)原理及調(diào)用方法

2021-11-20 11:41 更新

小白們?cè)趯W(xué)習(xí)遞歸函數(shù)的時(shí)候可能無法搞清楚遞歸函數(shù)的原理和運(yùn)行機(jī)制,遞歸函數(shù)是常用到的一類函數(shù),最基本的特點(diǎn)是函數(shù)自身調(diào)用自身,但必須在調(diào)用自身前有條件判斷,若滿足條件,則調(diào)用函數(shù)本身,若不滿足則終止本函數(shù)的自調(diào)用,然后把目前流程的主控權(quán)交回給上一層函數(shù)來執(zhí)行,否則就會(huì)無限調(diào)用下去。下面先介紹php實(shí)現(xiàn)遞歸函數(shù)的3種基本方式。


一、利用引用做參數(shù)

先不管引用做不做參數(shù),必須先明白引用到底是什么?引用不過是指兩個(gè)不同名的變量指向同一塊存儲(chǔ)地址。本來每個(gè)變量有各自的存儲(chǔ)地址,賦值刪除各行其道?,F(xiàn)在可好,兩個(gè)變量共享一塊存儲(chǔ)地址。 $a=&$b; 。實(shí)際上指的是 $a 不管不顧自己原來的存儲(chǔ)地址,非要和 $b 共享一室了。因而任何對(duì)存儲(chǔ)地址數(shù)值的改變都會(huì)影響兩個(gè)值。  
  
函數(shù)之間本來也是各行其是,即便是同名函數(shù)。遞歸函數(shù)是考慮將引用作為參數(shù),成為一個(gè)橋梁,形成兩個(gè)函數(shù)間的數(shù)據(jù)共享。雖然兩個(gè)函數(shù)見貌似操作的是不同地址,但是實(shí)際上操作的是一塊兒內(nèi)存地址。
function test($a=0,&$result=array()){
$a++;
if ($a<10) {
  $result[]=$a;
  test($a,$result);
}
echo $a;
return $result;
 
}
上面的例子非常簡答,以a<10作為判斷條件,條件成立,則把a(bǔ)賦給result[];將result的引用傳入函數(shù),會(huì)將每一次遞歸產(chǎn)生的a添加到結(jié)果數(shù)組result。因而本例生成的$result數(shù)組是 Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 [6] => 7 [7] => 8 [8] => 9 ) 。

本例比較有意思的是echo a的值。相信很多人認(rèn)為是12345678910吧,其實(shí)不然,是1098765432。為什么呢?因?yàn)楹瘮?shù)還沒執(zhí)行echoa前就進(jìn)行了下一次的函數(shù)遞歸。真正執(zhí)行echo a是當(dāng)a<10條件不滿足的時(shí)候,echo a,返回result,對(duì)于上一層而言,執(zhí)行完遞歸函數(shù),開始執(zhí)行本層的echo $a,依次類推。

二、利用全局變量

利用全局變量完成遞歸函數(shù),請(qǐng)確保你確實(shí)理解什么是全局變量。global在函數(shù)內(nèi)申明變量不過是外部變量的同名引用。變量的作用范圍仍然在本函數(shù)范圍內(nèi)。改變這些變量的值,外部同名變量的值自然也改變了。但一旦用了&,同名變量不再是同名引用。利用全局變量實(shí)現(xiàn)遞歸函數(shù)沒必要理解到這么深的一層,還保持原有對(duì)全局變量的看法就可以順理成章理解遞歸函數(shù)。
function test($a=0,$result=array()){
  global $result;
  $a++;
  if ($a<10) {
    $result[]=$a;
    test($a,$result);
  }
  return $result;
}

三、利用靜態(tài)變量

我們常常在類中見到static,今天我們把它利用到遞歸函數(shù)中。請(qǐng)記住static的作用:僅在第一次調(diào)用函數(shù)的時(shí)候?qū)ψ兞窟M(jìn)行初始化,并且保留變量值。
  
舉個(gè)例子:
function test(){
static $count=0;
echo $count;
 
$count++;
}
test();
test();
test();
test();
test();
這一段代碼的執(zhí)行結(jié)果是多少?是00000么?必然不是。是01234。首先第一次調(diào)用test(),static對(duì) $count 進(jìn)行初始化,其后每一次執(zhí)行完都會(huì)保留 $count 的值,不再進(jìn)行初始化,相當(dāng)于直接忽略了 static $count=0; 這一句。
  
因而將static應(yīng)用到遞歸函數(shù)作用可想而知。在將需要作為遞歸函數(shù)間作為“橋梁"的變量利用static進(jìn)行初始化,每一次遞歸都會(huì)保留"橋梁變量"的值。
function test($a=0){
  static $result=array();
  $a++;
  if ($a<10) {
    $result[]=$a;
    test($a);
  }
  return $result;
}

小結(jié)

所謂遞歸函數(shù),重點(diǎn)是如何處理函數(shù)調(diào)用自身是如何保證所需要的結(jié)果得以在函數(shù)間合理"傳遞",當(dāng)然也有不需要函數(shù)之間傳值得遞歸函數(shù),例如:
function test($a=0){
  $a++;
  if ($a<10) {
    echo $a;
 
    test($a);
  }
}

php函數(shù)遞歸調(diào)用實(shí)例

function arrContentReplact($array)
  {
  if(is_array($array))
  {
  foreach($array as $k => $v)
  {
  $array[$k] = arrContentReplact($array[$k]);
  }
  }else
  {
  $array = str_replace(
  array('<', '>'),
  array('{', '}'),
  $array
  );
  }
  return $array;
  }
  $arr = array(array("< 小剛>","< 小曉>",array("<小強(qiáng)>",array("<浪人>"))),"< 小飛>","< 小李>","< 小紅>");
  $arr3 = arrContentReplact($arr);
  echo "
";
 
  print_r($arr3);

  echo "
";
 
  ?>

PHP遞歸實(shí)現(xiàn)無限級(jí)分類

在一些復(fù)雜的系統(tǒng)中,要求對(duì)信息欄目進(jìn)行無限級(jí)的分類,以增強(qiáng)系統(tǒng)的靈活性。那么PHP是如何實(shí)現(xiàn)無限級(jí)分類的呢?下面介紹一下使用遞歸算法并結(jié)合mysql數(shù)據(jù)表實(shí)現(xiàn)無限級(jí)分類的方法。

1、Mysql
首先我們準(zhǔn)備一張數(shù)據(jù)表class,記錄商品分類信息。表中有三個(gè)字段,id:分類編號(hào),主鍵自增長;title:分類名稱;pid:所屬上級(jí)分類id。

class表結(jié)構(gòu):
CREATE TABLE IF NOT EXISTS `class` ( 
  `id` mediumint(6) NOT NULL AUTO_INCREMENT, 
  `title` varchar(30) NOT NULL, 
  `pid` mediumint(6) NOT NULL DEFAULT '0', 
  PRIMARY KEY (`id`) 
) ENGINE=MyISAM  DEFAULT CHARSET=utf8; 

插入數(shù)據(jù)后,如圖:

PHP遞歸


2、PHP
根據(jù)不同的需求,我們提供兩種不同格式的自定義函數(shù),一種是返回字符串,一種是返回?cái)?shù)組,兩種函數(shù)都使用了遞歸方法。先看返回字符串格式的函數(shù):
function get_str($id = 0) { 
    global $str; 
    $sql = "select id,title from class where pid= $id";  
    $result = mysql_query($sql);//查詢pid的子類的分類 
    if($result && mysql_affected_rows()){//如果有子類 
        $str .= '<ul>'; 
        while ($row = mysql_fetch_array($result)) { //循環(huán)記錄集 
            $str .= "<li>" . $row['id'] . "--" . $row['title'] . "</li>"; //構(gòu)建字符串 
            get_str($row['id']); //調(diào)用get_str(),將記錄集中的id參數(shù)傳入函數(shù)中,繼續(xù)查詢下級(jí) 
        } 
        $str .= '</ul>'; 
    } 
    return $str; 
} 
以上函數(shù)get_str()通過遞歸,不斷查詢下級(jí)分類,并最終返回字符串,大家可以根據(jù)項(xiàng)目需求修改其中的str,最終生成一個(gè)無限分級(jí)列表:
include_once('connect.php'); //連接數(shù)據(jù)庫,connect.php文件自己寫一個(gè)啊 
echo get_str(0); //輸出無限級(jí)分類 

效果如圖:

無限級(jí)分類

接著我們來看返回?cái)?shù)組格式的函數(shù),一樣要使用遞歸:
function get_array($id=0){ 
    $sql = "select id,title from class where pid= $id"; 
    $result = mysql_query($sql);//查詢子類 
    $arr = array(); 
    if($result && mysql_affected_rows()){//如果有子類 
        while($rows=mysql_fetch_assoc($result)){ //循環(huán)記錄集 
            $rows['list'] = get_array($rows['id']); //調(diào)用函數(shù),傳入?yún)?shù),繼續(xù)查詢下級(jí) 
            $arr[] = $rows; //組合數(shù)組 
        } 
        return $arr; 
    } 
} 
函數(shù)get_array()返回了數(shù)組,這是我們期待的,所以推薦使用get_array()得到數(shù)組,這樣一來,我們可以對(duì)數(shù)組進(jìn)行任意操作,比如我們可以將數(shù)組轉(zhuǎn)換成json格式的數(shù)據(jù)傳給前端頁面,前端頁面可以通過解析json數(shù)據(jù)靈活展示分類信息。比如樹形結(jié)構(gòu)的分類列表,下拉分類列表等。
include_once('connect.php'); //連接數(shù)據(jù)庫 
$list = get_array(0); //調(diào)用函數(shù) 
print_r($list); //輸出數(shù)組 

輸出效果:

遞歸函數(shù)

如果要輸出json格式的數(shù)據(jù),則可使用:
echo json_encode($list); 
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)