Отладка рекурсивного цикла while (php) - PullRequest
1 голос
/ 24 февраля 2012

Я пишу функцию, которая берет теги из шаблона усов и генерирует хеш (причина этого в том, чтобы иметь возможность взять любой данный шаблон и быстро показать разработчику, каковы ожидаемые переменные).

Я извлекаю теги в плоский массив (достаточно просто), но следующий шаг сложен - мне нужно превратить плоский массив в многомерный массив, чтобы указать вложенную переменную.

Вот мой примерплоский массив:

$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;
}

Я думаю, что ошибка может заключаться в том, что он попадает в первое «если» и возвращает весь путь из функции, но я не уверен, такжеЯ уверен, как это исправить.

Ответы [ 5 ]

3 голосов
/ 24 февраля 2012

Просто для удовольствия я решил сделать вам один без рекурсии и вместо него использовать ссылки (более эффективно, чем рекурсия, сохраняя псевдонимы элементов массива в стеке).Работает и с вложенными подмножествами.

$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 в выводе исчезнет.

2 голосов
/ 24 февраля 2012

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

  • Не включайте функции «count» в циклы, когда вы не должны этого делать (ваш «$ array» не обновляется, поэтому его размер остается неизменным от начала до конца)
  • Не используйте 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);
?>
2 голосов
/ 24 февраля 2012

Это может быть больше из того, что вы ищете (и немного ближе к истинной рекурсии), но я не проверял это, потому что у меня нет экземпляра PHP, с которым можно работать в данный момент

Использование:

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

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

Шаги:

  1. Если позиция больше, чем количество массивов, все готово.
  2. Если нам нужно вернуться в root, убрать метку и снова вызвать
  3. Если нам нужно перейти к тегу, добавьте тег и повторите вызов
  4. Если мы находимся в root, добавить ключ и пустую запись
  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);
    }
}
2 голосов
/ 24 февраля 2012

Я немного изменил вашу функцию в соответствии с вашими потребностями, посмотрите, работает ли она для вас:

<code>$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).'
';

У вас были некоторые проблемы с этим $ i ... Я дал его в качестве ссылки, чтобы он автоматически обновлялся с помощью системы функций, и использовал другой параметр, чтобы точно соответствовать следующему закрывающему тегу ..., так что это подтвердит.

1 голос
/ 24 февраля 2012

Выкл. Одной ошибкой

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

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

...