2012-02-24 108 views
1

我正在寫一個函數從小鬍子模板把標籤並生成一個散列(這樣做的原因是爲了能夠採取任何給定的模板,並很快顯示出開發商什麼預期變量是)。調試遞歸while循環(PHP)

我提取標籤成扁平陣列(容易不夠),但下一步是棘手 - 我需要把扁平陣列成多維數組以指示嵌套變量。

這裏是我的樣品平板陣列:

$arr = array(
    'one', 
    '#two', 
    'sub1', 
    'sub2', 
    '/two', 
    'three' 
); 

和預期輸出:

$newArray = array(
    'one'=>'', 
    'two'=>array(
    'sub1'=>'', 
    'sub2'=>'' 
    ), 
    'three'=>'' 
); 

我已經越來越近,但我還沒有應用。我認爲遞歸函數將是一條可行的路線(儘管我對不同的解決方案持開放態度)。以下是我迄今爲止:

function recurse($array, $i = 0) { 
    $nested = array(); 

    while ($i < count($array)): 
    $tag = $array[$i]; 

    if (preg_match('/\//',$tag)) { 
     return $nested; 
    } elseif (preg_match('/^#/',$tag)) { 
     $tag = str_replace('#','',$tag); 
     $nested[$tag] = recurse($array, $i+1); 
     $i+= count($nested[$tag])+1; 
    } else { 
     $nested[$tag] = ''; 
     $i++; 
    } 
    endwhile; 
    return $nested; 
} 

我認爲錯誤可能是它遇到第一個「如果」和返回所有出路的功能,但我不能確定,我也不是肯定如何解決它。

+0

你目前看到的結果是什麼? – afuzzyllama 2012-02-24 14:41:17

+0

它只建立它,直到'sub2' – 2012-02-24 14:42:37

+0

我不認爲它命中'if(preg_match('/ \ /',$ tag))'它給我一個錯誤也(警告); – khael 2012-02-24 14:49:39

回答

2

我修改你的函數一點,以配合您的需要S,看看它是否適合你:

$arr = array(
    'one', 
    '#two', 
    'sub1', 
    '#sub2', 
     'subsub1', 
     'subsub2', 
     'subsub3', 
     'subsub4', 
    '/sub2', 
    'sub3', 
    '/two', 
    'three' 
); 

function recurse($array, &$i, $current_tag = "") 
{ 
    $nested = array(); 

    while ($i < count($array)): 
     $tag = $array[$i]; 
     if ($tag == '/'.$current_tag) 
     { 
      $i++; 
      return $nested; 
     } 
     elseif (preg_match('/^#/',$tag)) 
     { 
      $tag = str_replace('#','',$tag); 
      $i++; 
      $nested[$tag] = recurse($array, $i, $tag); 
     } else 
     { 
      $nested[$tag] = ''; 
      $i++; 
     } 
    endwhile; 
    return $nested; 
} 

$i = 0; 
$a = recurse($arr, $i); 

echo '<pre>'.print_r($a, true).'</pre>'; 

你不得不與$我一些問題......我把它作爲參考,因此,它會自動與功能更新系統,以及使用其他參數完全匹配下一個結束標記...,以便驗證。

+0

有$ i作爲參考看起來是缺少的一塊。我需要更好地處理傳遞值作爲參考 - 我不經常這樣做,並且這個概念對我來說有點模糊(尤其是當涉及遞歸時)。 – 2012-02-24 15:54:43

+0

@PhilipSchweiger,我很高興它幫助了你,但你有另一個問題:你沒有檢查你的結束標記是否與最後一個開始標記匹配,結果任何結束標記都會結束你的子樹分析。 – khael 2012-02-24 16:46:12

+0

我將添加這個作爲Mustache.php新方法的核心併發送拉請求 - 我會在我的代碼中記下您的貢獻。 – 2012-02-24 17:08:59

1

差一錯誤

$tag = str_replace('#','',$tag); 
    $nested[$tag] = recurse($array, $i+1); 
    $i+= count($nested[$tag])+1; 

當您返回嵌套數組,你要跳過結束標記,所以它應該是$i += count($nested[$tag]) + 2;

+0

不幸的是,這看起來只適用於一層嵌套。代碼需要是不可知的嵌套級別。 – 2012-02-24 15:51:20

+0

啊,對。所以它必須是參考,是的,凱爾的答案看起來非常好。 – 2012-02-24 15:54:29

2

這可能是更多你正在尋找的東西(更接近真正的遞歸),但我沒有測試它,因爲我目前沒有一個PHP實例工作

用法:

$input = array(
    'one', 
    '#two', 
    'sub1', 
    'sub2', 
    '/two', 
    'three' 
); 

$result = array(); 
recurse($input, $result, '', 0); 

步驟:

  1. 如果位置比數組數越大,我們正在這樣做。
  2. 如果我們需要回去達根,去掉標籤,並再次呼籲
  3. 如果我們需要進入一個標籤,添加標籤,並再次呼籲
  4. 如果我們在根,添加鍵和空白項
  5. 如果我們在一個標籤,一個空白項中添加鍵值的標籤

代碼:

function recurse($input, &$result, $tag, $position) 
{ 
    if($position >= count($input)) 
    { 
     return; 
    } 

    if(preg_match('@\/@',$input[$position])) 
    { 
     recurse($input, $result, '', $position + 1); 
    } 
    else if (preg_match('@^#@',$input[$position])) 
    { 
     $result[substr($input[$position], 1)] = array(); 
     recurse($input, $result, substr($input[$position], 1), $position + 1); 
    } 
    else if($tag == '') 
    { 
     $result[$input[$position]] = ''; 
     recurse($input, $result, $tag, $position + 1); 
    } 
    else 
    { 
     $result[$tag][$input[$position]] = ''; 
     recurse($input, $result, $tag, $position + 1); 
    } 
} 
+0

嘗試http://writecodeonline.com/php/ :)在線php測試 – khael 2012-02-24 15:08:57

+0

謝謝 - 給你一個upvote,但接受其他anser,因爲它沒有$ position變量管理 – 2012-02-24 15:52:29

2

是的,遞歸功能就是這樣。一些建議:

  • 不包括「計數」功能,在循環中時,你有沒有做(你的「$陣列」沒有更新,所以他的身材還是從begening同爲末)
  • 在進行簡單比較時不要使用preg_match。
  • 使用引用,否則你應該快速得到一個內存錯誤,用在遞歸函數中的巨大數組。

這裏的其他方式做你想做的:

<?php 
function recurse(&$array, &$return = array(), &$i = 0, $limit = NULL) 
{ 
    if(!isset($limit)){ 
     $limit = count($array) ; 
    } 
    for(;$i < $limit;$i++){ 
     if($array[$i]{0} == '#'){ 
      //opening 
      $key = substr($array[$i++], 1) ; 
      $return[$key] = array(); 
      recurse($array, $return[$key], $i, $limit) ; 
     }elseif($array[$i]{0} == '/'){ 
      return ; 
     }else{ 
      //same level 
      $return[$array[$i]] = ''; 
     } 
    } 
} 

$arr = array(
    'one', 
    '#two', 
    'sub1', 
    '#t2', 
    'sub1.1', 
    'sub1.2', 
    '/t2', 
    'sub2', 
    '/two', 
    'three' 
); 
$nested = array(); 
recurse($arr, $nested); 
var_dump($nested); 
?> 
+0

Upvote爲好的提示。 – 2012-02-24 15:53:13

3

只是爲了好玩,我決定讓你一個沒有遞歸以及使用參考,而不是(更高效,遞歸,存儲數組元素堆棧上的別名)。也適用於嵌套子集。

$arr = array(
    'one', 
     '#two','sub1', 
      '#twotwo','sub1','sub2','/twotwo', 
     'sub2','/two', 
    'three' 
); 

$out = array(); 

$stack = array(); 
$sp = 0; 

$stack[$sp] = &$out; 

foreach ($arr as $item) { 
    $cur =& $stack[$sp]; 
    if ($item[0] == '#') { 
     $item = substr($item, 1); 
     $cur[$item] = array(); 
     $stack[++$sp] = &$cur[$item]; 
    } 
    elseif ($item[0] == '/') { 
     $sp--; 
    } 
    else { 
     $cur[] = $item; 
    } 
} 
var_dump($out); 

輸出:

array 
    0 => string 'one' (length=3) 
    'two' => & 
    array 
     0 => string 'sub1' (length=4) 
     'twotwo' => & 
     array 
      0 => string 'sub1' (length=4) 
      1 => string 'sub2' (length=4) 
     1 => string 'sub2' (length=4) 
    1 => string 'three' (length=5) 

你可以忽略你的地方,而不是簡單地看array& array輸出的事實。這表示在符號表中,該特定元素的引用計數大於1.

原因是$stack仍然保留引用。如果在返回輸出之前執行unset($stack);,則會刪除其他參考,並且輸出中的&將消失。

+0

Upvote努力。 – 2012-02-24 15:52:41

+0

@PhilipSchweiger:謝謝,剛剛更新了答案,解決了額外引用的奧祕。 (老的PHP手冊,教我的東西;)) – Leigh 2012-02-24 15:54:14