首頁  >  文章  >  後端開發  >  再理解PHP引用

再理解PHP引用

不言
不言原創
2018-04-14 14:51:561099瀏覽

 這篇文章主要介紹了再理解PHP引用,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

#起因:

#日常開發中,我們會碰到構造樹的需求,透過id,pid的關係去建構一個樹結構,然後對樹進行遍歷等操作。其實作方式分為兩種: 1. 遞歸, 2. 引用
而這兩個方法的優缺點也很明顯。

  1. 遞歸實現起來較容易,但是隨著數著資料量的增大,其效能很低。

  2. 引用本身概念好理解,性能也很好,但是用好它還是存在著一定的門檻,不太好寫。

寫本文的起因是,這幾天碰到非常好的一個解決方案,讓我重新理解了引用。透過本文,總結下自己的學習成果.ok,那直接上代碼了。

Practise

如果下面的程式碼,你看完就能理解了,說明你引用真是學到了, 你也可以直接跳過本文哈~。
function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children")
    {
        $tmp = []; //以id为健,$value为值的容器,可以很巧妙的判断根节点元素
        $tree = [];  
        //利用引用,对$data的数据进行操作
        foreach ($data as $key => &$value) {
                // 
                $tmp[$value['id']] = &$value;
                
                if (!isset($tmp[$value['pid']])) {
                    $tree[] = &$tmp[$value['id']];
                }else {
                     $temp = &$tmp[$value['pid']];
                     $temp[$child][] = &$value;
                }
                unset($temp, $value);
        }
        return $tree;
    }

ok,先不說其他的,你先拿下面的數據測試下這個方法.

$data= [
            ["id" => 1, "pid" => 0 , "name" => 'Universe'],
            ["id" => 2, "pid" => 1 , "name" => 'Earth'],
            ["id" => 3, "pid" => 2 , "name" => 'China'],
            ["id" => 4, "pid" => 3 , "name" => 'Beijing'],
];
補充:這個方法需要注意一點,需要父節點在前,不適合無序數據,所以如果是無序的,先得排序.

如果沒有意外,打印的結果,應該如下:

array(1) {
  [0]=>
  array(4) {
    ["id"]=>
    int(1)
    ["pid"]=>
    int(0)
    ["name"]=>
    string(8) "Universe"
    ["children"]=>
    array(1) {
      [0]=>
      array(4) {
        ["id"]=>
        int(2)
        ["pid"]=>
        int(1)
        ["name"]=>
        string(5) "Earth"
        ["children"]=>
        array(1) {
          [0]=>
          array(4) {
            ["id"]=>
            int(3)
            ["pid"]=>
            int(2)
            ["name"]=>
            string(5) "China"
            ["children"]=>
            array(1) {
              [0]=>
              array(3) {
                ["id"]=>
                int(4)
                ["pid"]=>
                int(3)
                ["name"]=>
                string(7) "Beijing"
              }
            }
          }
        }
      }
    }
  }
}

如果到此,你還想不明白,沒關係,我們一一來分析下.
其實要徹底弄清楚這個解決方案,需要理解二個部分。

  1. foreach賦值原則

  2. 所引用的原則

foreach
    $data = ["student", "teacher"];
    foreach ($data as $index => $item) {
    }

注意每次循環的時候, 是把$data[0]和$data[1] 的「值」複製一份再賦給$item

引用(一定要自己動手試驗下)
$a = 1;
$b = &$a;
$c = $b;
$c = 2;
猜猜看 $b = ?;

如果引用有疑問,點我

到此,如果你能理解上面foreach和引用,並且能理解這個解決方案的所有執行過程,那麼恭喜你,你學的真好! 但如果還是有困難,沒關係,咱們一步一步踏實的來.

Analysis

ok,深吸一口氣,跟著我的思路,咱們一步一步來.

  1. #首先咱們看下原函數

function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children")
    {
        $tmp = []; #以id为健,$value为值的容器,可以很巧妙的判断根节点元素
        $tree = [];  
        #利用引用,对$data的数据进行操作
        foreach ($data as $key => &$value) {
                
                #&$value取到$data元素对应值的引用
                
                $tmp[$value['id']] = &$value;
                
                #以$value['id']为键,&$value引用为值push到$tmp中,
                #这样可以巧妙的判断当前元素是否为根节点
                
                if (!isset($tmp[$value['pid']])) {
                    #将根节点push到$tree中
                    $tree[] = &$tmp[$value['id']];
                    
                }else {
                     #若当前元素的父节点存在于$tmp中, 引用获取$tmp中对应父节点的值
                     $temp = &$tmp[$value['pid']];
                     #然后将当前元素push到其父节点的children中
                     $temp[$child][] = &$value;
                }
                #为了不引起变量污染, 引用用完后,需要unset掉
                unset($temp, $value);
        }
        return $tree;
    }
  1. 第一次迴圈

function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children")
    {
       # $tmp = [];
       # $tree = [];  
        
       # foreach ($data as $key => &$value) {
                // 
                $tmp[$value['id']] = &$value;
                
                if (!isset($tmp[$value['pid']])) {
                    $tree[] = &$tmp[$value['id']];
                }else {
        #             $temp = &$tmp[$value['pid']];
        #             $temp[$child][] = &$value;
        #        }
                unset($temp, $value);
        }
        return $tree;
    }

變數狀況:
$data[0] = ["id" => 1, "pid" = > 0 , "name" => 'Universe'];
$tmp[1] = &$data[0];
$tree[] = &$data[0]

  1. #第二次循環

function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children")
    {
       # $tmp = [];
       # $tree = [];  
        
       # foreach ($data as $key => &$value) {
                // 
                $tmp[$value['id']] = &$value;
                
        #        if (!isset($tmp[$value['pid']])) {
        #            $tree[] = &$tmp[$value['id']];
                }else {
                     $temp = &$tmp[$value['pid']];
                     $temp[$child][] = &$value;
                }
                unset($temp, $value);
        }
        return $tree;
    }

變數狀況:
$data[1] = ["id" => 2 , "pid" => 1 , "name" => 'Earth'];
$value=&$data[1];
$tmp[2] = &$data[1];
注意:
$temp即&$tmp[1],即和$data[0]指向相同的位址
所以$temp['children'][] = &$ value  ,操作的結果是:

$data[
    [  
     "id" => 1,
     "pid" => 0 ,
     "name" => 'Universe'
     "children"=>[
                 &$data[1],   //注意:存储的是引用
         ]
     ]   
     ...
]

4.第三次迴圈

function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children")
    {
       # $tmp = [];
       # $tree = [];  
        
       # foreach ($data as $key => &$value) {
                // 
                $tmp[$value['id']] = &$value;
                
        #        if (!isset($tmp[$value['pid']])) {
        #            $tree[] = &$tmp[$value['id']];
                }else {
                     $temp = &$tmp[$value['pid']];
                     $temp[$child][] = &$value;
                }
                unset($temp, $value);
        }
        return $tree;
    }

變數狀況:
$data[2] = ["id" = > 3, "pid" => 2 , "name" => 'China'];
$value = &$data[2];
$tmp[3] = &$data[2 ];
注意:
$temp即&$tmp[2],即和$data[1]指向相同的位址

所以$temp['children' ][] = &$value  ,操作的結果是:
這裡注意一下:
這是第二次循環的時候,children中儲存的$data[1]的引用

$data[
    [  
     "id" => 1,
     "pid" => 0 ,
     "name" => 'Universe'
     "children"=>[
                 &$data[1],   //注意:存储的是引用
         ]
     ]   
     ...
]

第三次循環的時候,則是$data[1]['children'][] = &$value, 而$value指向的是$data[2]
,所以結果是:

   $data[
        [  
         "id" => 1,
         "pid" => 0 ,
         "name" => 'Universe'
         "children"=>[
                       // &$data[1],   //注意:存储的是引用
                         [
                           "id" => 2, 
                           "pid" => 1 ,
                           "name" => 'Earth'
                           "children" => [
                                   &data[2] //注意:存储的是引用   
                            ]
                         
                         ] 
                         
                     ]
             ]
         ]   
         ...
    ]

5.第四次迴圈

function buildTreeByReference($data, $id = 'id', $pid = 'pid', $child = "children")
    {
       # $tmp = [];
       # $tree = [];  
        
       # foreach ($data as $key => &$value) {
                // 
                $tmp[$value['id']] = &$value;
                
        #        if (!isset($tmp[$value['pid']])) {
        #            $tree[] = &$tmp[$value['id']];
                }else {
                     $temp = &$tmp[$value['pid']];
                     $temp[$child][] = &$value;
                }
                unset($temp, $value);
        }
        return $tree;
    }

變數狀況:
$data[3] = ["id" => 4, "pid" => 3 , "name" => 'Beijing'];
$value = &$data[3];
$tmp[3] = &$data[3];
注意:
$temp即&$tmp[2],即和$data[1]指向相同的位址

所以$temp['children'][] = &$value  ,操作的結果是:
這裡注意一下:
這是第三次循環的時候,children中儲存的$data[2]的引用

   $data[
        [  
         "id" => 1,
         "pid" => 0 ,
         "name" => 'Universe'
         "children"=>[
                       // &$data[1],   //注意:存储的是引用
                         [
                           "id" => 2, 
                           "pid" => 1 ,
                           "name" => 'Earth'
                           "children" => [
                                   &data[2] //注意:存储的是引用   
                            ]
                         
                         ] 
                         
                     ]
             ]
         ]   
         ...
    ]

第四次循環的的時候,則是$ data[2]['children'][] = &$value, 而$value指向的是$data[3]
,所以結果是:

   $data[
        [  
         "id" => 1,
         "pid" => 0 ,
         "name" => 'Universe'
         "children"=>[
                       // &$data[1],   //注意:存储的是引用
                         [
                           "id" => 2, 
                           "pid" => 1 ,
                           "name" => 'Earth'
                           "children" => [
                                  // &data[2] //注意:存储的是引用 
                                  [
                                      "id" => 3,
                                      "pid" => 2 ,
                                      "name" => 'China' 
                                      "children" =>[
                                                 &$data[3];  //注意:存储的是引用                            
                                      ]
                                  ]  
                            ]
                         
                         ] 
                         
                     ]
             ]
         ]   
         ...
    ]

ok,至此,整個執行過程走通了,你懂了嗎? :)

對了,還另外一個方法,也是透過引用的,這個我就不分析,要是理解上面的方法,下面的相對來說簡單些。

    public static function buildTreeByReference1($data, $id = 'id', $pid = 'pid', $child = "children")
    {
        $tmp = [];
        
        foreach ($data as $key => $value) {
            $tmp[$value[$id]] = $value;
        }
        $tree = [];
        foreach ($tmp as $key => $value) {
                if (isset($tmp[$value['pid']])) {
                    $tmp[$value['pid']]['children'][] = &$tmp[$key];
                }else{
                    $tree[] = &$tmp[$key];    
                }
        }
        return $tree;
    }

相關建議:

php引用的幾個用法實例總結                              c              

以上是再理解PHP引用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn