调试递归while循环(php)

时间:2012-02-24 14:34:10

标签: php algorithm recursion while-loop mustache

我正在编写一个函数来从胡子模板中获取标签并生成哈希(这样做的原因是能够获取任何给定的模板并快速向开发人员显示预期变量是什么)。

我将标签提取到平面数组中(很容易),但下一步很棘手 - 我需要将平面数组转换为多维数组以指示嵌套变量。

这是我的样本平面阵列:

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

我认为错误可能是它击中了第一个'if'并且一直返回函数,但我不确定,也不确定如何解决它。

5 个答案:

答案 0 :(得分: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);,则会删除其他引用,并且输出中的&将消失。

答案 1 :(得分:2)

这可能是你正在寻找的更多(并且更接近真正的递归),但我没有测试它,因为我现在没有PHP实例可以解决

用法:

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

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

步骤:

  1. 如果位置大于数组计数,我们就完成了。
  2. 如果我们需要返回root用户,请删除代码并再次致电
  3. 如果我们需要进入标记,请添加标记并再次调用
  4. 如果我们在root中,请添加密钥和空白条目
  5. 如果我们在标记中,请使用空白条目将标记添加到标记
  6. 代码:

    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 :(得分:2)

我根据您的需要修改了您的功能,看看它是否适合您:

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

你对这个$ i有一些问题...我把它作为参考,以便它会自动用功能系统更新,并使用另一个参数来准确匹配下一个结束标签......,这样它就会验证

答案 3 :(得分:2)

是的,recurse功能就是这样。一些建议:

  • 当你不做时,不要在循环中包含“count”函数(你的“$ array”没有更新,所以从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);
?>

答案 4 :(得分:1)

关闭一个错误

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

当您返回嵌套数组时,您必须跳过结束标记,因此它应该是$i += count($nested[$tag]) + 2;