【问题标题】:How to detect and avoid infinite loop in PHP如何检测和避免 PHP 中的无限循环
【发布时间】:2016-09-26 21:41:32
【问题描述】:

这个问题不是关于我的 PHP 代码中的错误(目前我没有任何 PHP 脚本,我仍在考虑算法)。问题来了:

我目前正在开发一个机械部件管理器,它能够基于内部零件构建一个机械部件(例如,我有一个自行车部件,对于那个部件,我需要 2 个轮子,1车把,对于车轮,我需要轮胎等)。

每个内部零件也是我数据库中的一个机械部件,并与一个唯一 ID 链接(并且有一个包含许多 PDF、许多 3D 文件等的文件夹)。

我有一个 GUI(HTML 格式)代表我的数据库,每个部分都有一个“构建”按钮来收集构建内部部分所需的所有文件。

例如:

我的自行车的 ID 为 n°1,车轮的 ID 为 n°2,车把的 ID 为 n°3。

该算法非常简单,但存在无限循环漏洞。

我该如何避免这种情况:如果我的自行车 (id 1) 需要一个轮子 (id 2),而我的轮子需要一个自行车...需要一个需要自行车的轮子... ..?

非常感谢,

【问题讨论】:

  • 为什么您的车轮需要自行车?轮子是可以用于更多产品、自行车、汽车、溜冰鞋、滑板的零件。轮子可以自己存在,但自行车不能。
  • 刚刚发现有点相关:here,把父母当成自行车,把轮子当成孩子。
  • 我假设将来我的数据库会有数千篇文章,很容易出错。如果由我来决定,我不会在意,但是……我为关心的人工作……啊哈
  • 您需要在实例化新部件时跟踪并检查它是否不存在于它的父对象中 - 这是在您的情况下避免无限循环的简单方法。希望它对你有意义。
  • 这个问题和算法标签有关吗?

标签: php algorithm


【解决方案1】:

在执行构建函数期间,您只需跟踪您已经为其生成结果的所有组件(在哈希中),如果您再次遇到其中一个,您只需忽略它。

以下是一些样板代码,您可以从中获得灵感:

// Sample "database":
$components = array(
    1 => array (
        "id" => 1,
        "name" => "bike",
        "needs" => array (
            array ("id" => 2, "count" => 2), // 2 wheels
            array ("id" => 3, "count" => 1), // 1 handlebar
        ),
        "folder" => "/my/folders/bike"
    ),
    2 => array(
        "id" => 2,
        "name" => "weel",
        "needs" => array(
            array("id" => 4, "count" => 1), // 1 tire
            array("id" => 1, "count" => 1)  // 1 wheel?? - erroneous information!
        ),
        "folder" => "/my/folders/wheel"
    ),
    3 => array(
        "id" => 3,
        "name" => "handlebar",
        "needs" => array (),
        "folder" => "/my/folders/handlebar"
    ),
    4 => array(
        "id" => 4,
        "name" => "tire",
        "needs" => array(),
        "folder" => "/my/folders/tire"
    )
);  

// Build function: returns a list of folders related
// to all the parts of the given component.
function build($componentId, $components, $hash = array()) {
    // Protection against infinite recursion:
    if (isset($hash[$componentId])) return []; 
    $elem = $components[$componentId];
    // set hash, keyed by component ID.
    $hash[$componentId] = 1;
    // Add folder for this component
    $folders[] = $elem['folder'];
    // Collect folders of dependent components recursively
    foreach($elem['needs'] as $child ) {
        // ... pass the hash as third argument
        $folders = array_merge($folders, build($child["id"], $components, $hash));
    }
    return $folders;
}

// Example call: build component with ID 1, returning a list of folders:
print_r (build(1, $components));

上述代码的输出将是:

Array
(
    [0] => /my/folders/bike
    [1] => /my/folders/wheel
    [2] => /my/folders/tire
    [3] => /my/folders/handlebar
)

所以当第二次遇到自行车时,它就被忽略了。

【讨论】:

  • 非常感谢,我使用了你的解决方案,但我做了一些更新,我不做 $hash[$componentId] = 1;但是 $hash[] = $componentId,然后我使用 in_array() 而不是 isset() !非常感谢,你拯救了我的一天。
  • 不客气。您的变体也将起作用,但速度较慢,因为 in_array 可能必须搜索整个数组,而 isset 以恒定时间运行。但是,在只有大约 100 个组件的数据集上,差异不会那么显着。如果您有 1000 个或更多组件,就会变得引人注目。
【解决方案2】:

你应该区分父子关系。

自行车可以包含一个轮子作为子项,而轮子可以包含一个自行车作为其父项。一个螺丝和一个轮胎也可以是车轮的子项目。

导航应该有一个方向,从父项到子项或相反。

【讨论】:

  • OP 希望能够通过单击自行车的任何部件来创建自行车。这意味着在这种情况下,导航确实有多个方向。
猜你喜欢
  • 2019-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-23
相关资源
最近更新 更多