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