2012-02-24 4 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

+0

Он только строит его до «sub2» –

+0

Я не думаю, что он попадает в 'if (preg_match ('/ \ /', $ tag))' он также дает мне об ошибке (предупреждение); – khael

ответ

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, как ссылка выглядит отсутствующий кусок. Мне нужно получить более эффективный способ передачи значений в качестве ссылки - я не часто это делаю, и концепция немного туманна для меня (особенно при рекурсии). –

+0

@PhilipSchweiger, я рад, что это помогло вам, у вас возникла еще одна проблема: вы не проверяли, соответствовал ли ваш закрывающий тег последнему открывающему тегу, вследствие чего любой закрывающий тег завершил бы ваш синтаксический разбор поддерева. – khael

+0

Я собираюсь добавить это как ядро ​​нового метода в Mustache.php и отправить запрос на перенос - я заметлю ваш вклад в мой код. –

1

Off одной ошибки

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

При возврате вложенного массива, вы должны пропустить закрывающий тег, поэтому он должен быть $i += count($nested[$tag]) + 2;.

+0

Похоже, это работает только для одного уровня гнездования, к сожалению. Код должен быть агностическим уровнем уровней гнездования. –

+0

А, правильно. Так что это должны быть ссылки, да, ответ khael выглядит очень хорошо. –

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

+0

Спасибо - дал вам верх, но принял другой anser, поскольку он управлялся без переменной $ position –

2

Да, функция рекурсии - это путь.Некоторые советы:

  • Не включать «подсчет» функции в циклах, когда вам не нужно делать (ваш «$ массив» не обновляется, так что его размер все тот же от begening до конца)
  • Не используйте preg_match, если у вас есть простое сравнение.
  • Используйте ссылки, иначе вы должны быстро получить ошибку памяти с огромными массивами, используемыми в функциях recurse.

Вот другой способ сделать то, что вы хотите:

<?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 для хороших советов. –

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); перед возвратом вывода, дополнительные ссылки удаляются, и вывод & s на выходе исчезнет.

+0

Upvote для усилий. –

+0

@PhilipSchweiger: Спасибо, просто обновил ответ тоже, решил тайну дополнительных ссылок. (хорошее старое руководство PHP, учит меня вещам;)) – Leigh