在各種欄目以及分類設置中,無限分類經常會被用到,而無限分類在進行排序的時候必然要用到遞歸,這里進行一次詳細的分析解讀。
首先我們先了解一下遞歸函數:
遞歸函數在語言學習的時候會單獨拿出來學習,因為它非常常用,本質上來講遞歸函數就是調用自己的函數。
舉個例子:
<?phpfunction test(){ static $a=0; if($a<10){ $a++; test(); } echo $a."<br/>";}test();test函數里面又再調用了自身,這就是俗稱的遞歸函數!遞歸函數都有條件設置,不然的會無限循環下去,這樣會導致程序奔潰。
所以一般來講我總結遞歸函數有兩個特點:
一個是記錄條件值,記錄的條件值必須保證不會再下一次調用時丟失。
test函數里$a便是記錄條件值,它是依靠使用static關鍵字來保證記錄每次增加的數值不會再下一次調用test()函數而丟失,因為函數中static修飾的變量僅僅在第一次初始化,并保留變量值。所以只要保證這一點,不光static,其他的方式也可以達到目的,例如global還有&修飾符。
另一個是條件檢查。test里面對$a大小的限制就是該條件的檢查過程。
test函數中if($a<10)就是這個條件檢查的過程,它限制了test()函數對自身的調用,這樣就可以防止無限調用導致程序奔潰。
函數a內部調用另外的函數b,如果b函數沒有完成,那么a函數就會一直等待下去,直到b函數完成,才會回到a函數繼續執行。遞歸的過程中利用了這個特性,正是這個能幫助我們對無限分類進行排序,用上面test()遞歸函數說明:
上面test()打印出來的結果是:
10,10,10,10,10,10,10,10,10,10,10
可能和很多人想的不一樣,大概有許多人會覺得不應該是0,1,2,3,4,5,6,7,8,9,10這樣的結果嗎?
這是因為函數test調用過程中,只要$a<10,就會調用自身的test(),每次調用的test()都沒有到達echo處,也就是每次調用的test函數并沒有完結,直到$a遞增到了10,才第一次echo $a,這個時候$a已經是10了,因此第一個10實際是遞歸了11次后的$a,第十一次遞歸的test由于不符合<10的條件該函數完結,這個時候才開始回到遞歸第十次test函數里執行echo,這個時候由于$a是靜態變量,值已經是10了,因此echo出的結果是10,下面依次回到之前的test函數完成前面未完成的echo步驟,因此echo出11個10,最后一個10實際是第一次執行test函數的echo結果。
無限分類的排序完成也是用的這個原理,遞歸排序函數不斷的通過parentid等于上一級id的子類來匹配該類別的子類別,只要找到第一個子類,就用找到的這個子類的id去找下一級的子類,直到沒有更下級的子類的時候,才返回上一級接著繼續找,找到后又開始尋找該子類下一級子類,直到沒有為止才返回,這個過程不斷循環。可能用文字不太能理解,下面的實例中我會畫出圖例,請先往后看。
我們先來看無限分類的數據er圖:
例如一個裙子的類目,它有父類別女裝,女裝又屬于衣服的類目,假定裙子的id為3,女裝為2,衣服為1,那么裙子的parentid就是直接的上級類目裙子的id,因此parentid=2,而child是裙子所在的樹形結構上全部的祖先元素id,那么child應該是1,2,3,因此裙子的深度deep為3,這里我假設的是用,隔開,也可以用其他的符號隔開,title不用說就是裙子。
具體我們來看這個樹形結構:
從樹形結構分類還可以繼續延伸下去,上一級的id是下一級的parentid,就是通過id和parent這兩個列來實現基本的無限分類的,再進行無限分類的遞歸排序的時候也是依靠這兩個字段的關系。
下面是一個無限分類的表結構以及數據的例子:
為了更清楚下面畫了樹形圖:
上一代的類別的id就是自身的parentid,最高一級的parentid則為0,這點非常重要,是無限分類的排序的依據。
我們所要的無限分類排序效果應該是各欄目下的子欄目都放到該欄目下方,用id進行排序,我們要的效果就是下面這樣,為了方便查看我用了tab以及|——來間隔區分欄目之間的關系:
衣服
|——男裝
|——休閑上衣
|——短袖
|——長袖
|——休閑褲
|——女裝
|——女裝上衣
|——女裝下裝
|——牛仔褲
|——裙子
由于類別可以無限延伸下去,所以這里明顯我們需要使用遞歸函數進行排序分類。
獲取數據所有結果,并按id排序
$MySQLi=new mysqli('localhost','root','root','test')or die("連接失敗");$mysqli->set_charset("utf8");$re=$mysqli->query("select * from col order by id asc");$result=$re->fetch_all(MYSQLI_ASSOC);遞歸排序函數
function recursion($result,$parentid=0){ /*記錄排序后的類別數組*/ static $list=array(); foreach ($result as $k => $v){ if($v['parentid']==$parentid){ /*將該類別的數據放入list中*/ $list[]=$v; recursion($result,$v['id']); } } return $list;}這個時候我們就能得到按類別排序好的數組了,但是如果要有相應的格式,例如上面的|——,就需要對函數進行改進。function recursion($result,$parentid=0,$format="|--"){ /*記錄排序后的類別數組*/ static $list=array(); foreach ($result as $k => $v){ if($v['parentid']==$parentid){ if($parentid!=0){ $v['title']=$format.$v['title']; } /*將該類別的數據放入list中*/ $list[]=$v; recursion($result,$v['id']," ".$format); } } return $list;}$list=recursion($result,0,'|--');這樣排序的結果如下:
整個遞歸函數的執行情況大概是什么樣的,我們接上面說過的要畫的實例圖,看圖后就很清楚這個原理了,這個圖只畫了部分,不過足夠理解這個過程了:
新聞熱點
疑難解答
圖片精選