遞迴教程:一份可疑的曖昧名單

clark發表於2021-12-22

最近處理了一次無限分類的資料,主要是用到了遞迴函式,當然laravel有更優雅的關聯查詢辦法,不過由於資料需要有很多其他二次修改,於是採用了原生的遞迴處理,同時對於遞迴的原理,採用一個形象的比喻來說明:
(核心思路:遞迴有去有回)

官方說明

遞迴函式即自呼叫函式,也就是函式在函式體內部直接或間接地自己呼叫自己。需要注意的是使用遞迴函式時通常會在函式體中附加一個判斷條件,以判斷是否需要繼續執行遞迴呼叫,當條件滿足時會終止函式的遞迴呼叫。

背景說明

突然的一天,現任女友發現你還和以前的一些女性朋友保持聯絡,並且找到了一份證據擺到你的面前,讓你解釋一下你跟他們之間的關係,相關證據如下:

<?php
//出處:https://www.dongyao.ren/
$arr = [
    [
        'id' =>1,
        'pid'=>0,
        'name' => '前女友'
    ],
    [
        'id' =>2,
        'pid'=>1,
        'name' =>'二毛'
    ],
    [
        'id' =>3,
        'pid'=>0,
        'name' =>'可疑人員'
    ],
    [
        'id' =>4,
        'pid'=>2,
        'name' =>'小紅'
    ],
    [
        'id' =>5,
        'pid'=>2,
        'name' =>'小綠'
    ],
    [
        'id' =>6,
        'pid'=>3,
        'name' =>'同事'
    ],
    [
        'id' =>7,
        'pid'=>1,
        'name' =>'大毛'
    ],
    [
        'id' =>8,
        'pid'=>3,
        'name' =>'同學'
    ]
];

相關證人

現任女友嚴厲發話了,你自己交代後果可能會好一點,要不然她就要去問下面這個證人朋友了:

<?php
//出處:https://www.dongyao.ren/
getMenu($menus_main,$parent_id=0,$sub='children',$level=1){
        $data = array();
        foreach($menus_main as $key=>$val){
            if($val['parent_id']==$parent_id){
                $val['level']=$level;
                $val[$sub]=$this->getMenu($menus_main,$val['id'],$sub,$level+1);
                $data[] = $val;
            }
        }
        return $data;
    }

主動交代

以下是這些名單的關係圖譜,目前已經一頭冷汗了

回憶一下細節吧

現任要求你講述一下跟這些人的認識細節,已經證據確鑿了,就別硬撐了
把剛才那個證人來過來一起回憶一下,經過討論,確定了一個思路,如下:

<?php
//出處:https://www.dongyao.ren/
function getMenu($menus_main,$pid=0,$sub='children',$level=1,$name = '主欄目'){
    $data = array();
    foreach($menus_main as $key=>$val){

        echo "正在第".$level."層排查第".$val['id']."號人物(Pid:".$val['pid']."):<span style='color:red'><b>".$val['name']."</b></span>屬於【".$name."】---此時data值".json_encode($data,JSON_UNESCAPED_UNICODE);

        if($val['pid']==$pid){
            echo "<span style='color:green'>:證據確鑿,繼續排查".($level+1)."層</span><hr>";
            $val['level']=$level;
            // unset($menus_main[$key]);
            $val[$sub]=getMenu($menus_main,$val['id'],$sub,$level+1,$val['name']);
            $data[] = $val;
        }else{
            if($val['id'] == $pid){
                echo "<span style='color:red'>---條件不滿足【自己查自己】,開始返回---</span><hr>";
            }else{
                echo "<span style='color:red'>---條件不滿足【不是同一類】,開始返回---</span><hr>";
            }

        }

    }
    return $data;
}

得到了具體的聊天過程如下:


……還有好多沒列出來

內容過多,一口氣說不完,說重點

相關過程

對照關係圖譜和這一份聊天記錄可以看到:

由於過程比較緊張,手抖畫的圖,大概意思就是這麼一個簡化圖

  • 首先對號人物前女友進行排查,確定這是個大號人物。此時由於不確定這個大人物下面還有沒有其他相關人物,所以,先放在待定區此時$data變數還未賦值
  • 第二輪就是排查所有人是不是都跟前女友有關係,一個一個審問之後,暫時排除無關人員;
  • 剛開始就遇上自己查自己的情況,跳過,該人物第一步已經確定了
  • 第二位二毛進入視線了,確定跟前女友有關,滿足調查條件,此時二毛後面的人員,先原地等待不要動,等查完二毛再處理
  • 此時進入調查二毛的關係鏈工作中,繼續把8位關鍵人物拉過來,詢問是否跟二毛有關係
  • 問到小紅這裡,發現小紅是二毛的孩子,滿足調查條件, 繼續把8位關鍵人物拉過來,詢問是否跟小紅有關係
  • 發現沒有人跟小紅有關係了,此時就把小紅二毛孩子的資訊標記到$data變數當中

到這一步,其中一條關係鏈已經調查清除,賦值$data,當前只有小紅一人被標記賦值,當前其他關係鏈的相關人物還在等待調查,繼續往下看:

  • 回過頭去,繼續到上次中斷調查的位置,從小紅往後繼續調查,然後發現了小綠也是二毛的孩子,後續工作跟小紅一樣。
  • 得出小綠沒有關聯人物了,退出當前關係鏈,將調查結果追加到上一步的後面

此時,$data變數當中包含兩個人物了,有小紅,還有小綠,後續工作繼續

  • 回到上次中斷的位置,$data當前包含二毛和兩個孩子的總資訊,然後繼續從二毛後面調查,發現大毛也是前女友陣營的,後續關係鏈調查跟二毛一樣:

一圈調查下來,發現大毛還算簡單,沒有孩子,結束對他的調查,把大毛的調查結果,記錄在$data當中,此時data變數包含了大毛和二毛,以及他們的孩子關係資訊。

……
後續其他關係鏈的邏輯同上,反反覆覆,一個一個的審問調查,發現可疑就暫時中斷,後續的,順著可疑人員排查下去,排查完之後,登記排查結果,回過頭再來從中斷位置繼續深挖下去,如此反覆。

證據鏈

下方是本次過程的所有程式碼,各位可以自行執行參閱

<?php
//出處:https://www.dongyao.ren/
function getMenu($menus_main,$pid=0,$sub='children',$level=1,$name = '主欄目'){
    $data = array();
    foreach($menus_main as $key=>$val){

        echo "正在第".$level."層排查第".$val['id']."號人物(Pid:".$val['pid']."):<span style='color:red'><b>".$val['name']."</b></span>屬於【".$name."】---此時data值".json_encode($data,JSON_UNESCAPED_UNICODE);

        if($val['pid']==$pid){
            echo "<span style='color:green'>:證據確鑿,繼續排查".($level+1)."層</span><hr>";
            $val['level']=$level;
            // unset($menus_main[$key]);
            $val[$sub]=getMenu($menus_main,$val['id'],$sub,$level+1,$val['name']);
            $data[] = $val;
        }else{
            if($val['id'] == $pid){
                echo "<span style='color:red'>---條件不滿足【自己查自己】,開始返回---</span><hr>";
            }else{
                echo "<span style='color:red'>---條件不滿足【不是同一類】,開始返回---</span><hr>";
            }

        }

    }
    return $data;
}


$arr = [
    [
        'id' =>1,
        'pid'=>0,
        'name' => '前女友'
    ],
    [
        'id' =>2,
        'pid'=>1,
        'name' =>'二毛'
    ],
    [
        'id' =>3,
        'pid'=>0,
        'name' =>'可疑人員'
    ],
    [
        'id' =>4,
        'pid'=>2,
        'name' =>'小紅'
    ],
    [
        'id' =>5,
        'pid'=>2,
        'name' =>'小綠'
    ],
    [
        'id' =>6,
        'pid'=>3,
        'name' =>'同事'
    ],
    [
        'id' =>7,
        'pid'=>1,
        'name' =>'大毛'
    ],
    [
        'id' =>8,
        'pid'=>3,
        'name' =>'同學'
    ]
];
getMenu($arr);return;

AD

歡迎訪問部落格:www.dongyao.ren

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章