调试递归 while 循环 (php)

发布于 2025-01-08 10:24:37 字数 988 浏览 1 评论 0原文

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

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

这是我的示例平面数组:

$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”并从函数中一路返回,但我不确定,也不知道如何修复它。

I'm writing a function to take the tags from a mustache template and generate a hash (the reason for this is to be able to take any given template and quickly show a developer what the expected variables are).

I extract the tags into a flat array (easy enough), but the next step is tricky - I need to turn the flat array into a multi-dimensional array to indicate nested variable.

Here's my sample flat array:

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

And the expected output:

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

I have been getting close, but am not quite there yet. I thought a recursive function would be the way to go (though I am open to a different solution). Here is what I have so far:

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

I think the bug may be that it hits the first 'if' and returns all the way out of the function, but I'm not certain, nor am I sure how to fix it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

人生戏 2025-01-15 10:24:37

只是为了好玩,我决定为您制作一个无递归并使用引用代替(比递归更有效,将数组元素别名存储在堆栈上)。也适用于嵌套子集。

$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)

您可以忽略在输出中看到的事实 &数组代替简单的数组。这表示在符号表中该特定元素的引用计数>。 1.

原因是$stack仍然维护着一个引用。如果您在返回输出之前执行 unset($stack);,则附加引用将被删除,输出中的 & 将消失。

Just for fun I decided to make you one without recursion and using references instead (more efficient that recursion, storing array element aliases on a stack). Works with nested subsets too.

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

Output:

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)

You can ignore the fact in the output you see & array in places instead of simply array. This signifies that in the symbol table the reference count for that particular element is > 1.

The reason for this is that $stack is still maintaining a reference. If you do an unset($stack); before returning the output, the additional references are removed and the &s in the output will disappear.

终止放荡 2025-01-15 10:24:37

我对你的函数进行了一些修改以满足你的需求,看看它是否适合你:

$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 有一些问题...我将它作为参考,以便它会自动随函数系统更新,并使用另一个参数与下一个结束标记完全匹配...,以便它有效。

I modified your function a bit to match your needs, see if it works for you:

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

You had some issues with that $i... I gave it as reference so that it will automatically update with the function system, and used another parameter to match exactly the next closing tag..., so that it will validate.

淡写薰衣草的香 2025-01-15 10:24:37

是的,递归函数就是这样。一些建议:

  • 当你不需要做的时候,不要在循环中包含“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);
?>

Yes, recurse function is the way. Some advices :

  • Do not include "count" functions in loops when you have not to do (your "$array" is not updated, so his size still the same from the begening to the end)
  • Do not use preg_match when you have simple comparison to do.
  • Use references, else you should quickly get a memory error with huge arrays used in recurse functions.

Here an other way to do what you want to :

<?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);
?>
萌吟 2025-01-15 10:24:37

这可能更符合您的需求(并且更接近真正的递归),但我没有测试它,因为我目前没有可以使用的 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);
    }
}

This might be more of what you are looking for (and a little more closer to true recursion), but I didn't test it because I don't have a PHP instance to work off of at the moment

Usage:

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

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

Steps:

  1. If the position is greater than the array count, we are done.
  2. If we need to go back up to root, remove tag and call again
  3. If we need to go into a tag, add tag and call again
  4. If we are in root, add the key and blank entry
  5. If we are in a tag, add the key to the tag with a blank entry

Code:

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);
    }
}
以可爱出名 2025-01-15 10:24:37

关闭一个错误

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

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

Off by one error

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

When you return the nested array, you have to skip over the closing tag, so it should be $i += count($nested[$tag]) + 2;.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文