【问题标题】:Suggestions for properly iterating through a vector正确迭代向量的建议
【发布时间】:2011-04-06 04:01:18
【问题描述】:

我编写了一个程序来确定井字游戏的游戏树。 我相信大部分代码都井井有条。 我编写了一个函数来比较向量的元素以确定是否有任何元素重复。 重复的项目可以是真正相同的,也可以是对称的。 从向量中删除重复的元素。 我的比较功能似乎存在错误地消除元素的问题。 请看看我是如何遍历向量的,看看语法/逻辑是否合理。

我的猜测是使用 运算符可能是问题的一部分。 该函数的基本逻辑是将第一个元素与最后一个元素进行比较,然后是倒数第二个元素,依此类推。在将第一个元素与所有元素进行比较后,您再次开始将第二个元素与其他元素进行比较,依此类推...

void compareAllGames(move& aMove) { /* aMove is a vector of games. games are a struct of data */
    vector<game>:: iterator frontIter = aMove.begin();
    vector<game>:: iterator rearIter = aMove.end() - 1;
    vector<game>:: iterator compIter;
    for (; frontIter < rearIter; frontIter++) { /* move along the games from first to last */
        for (compIter = aMove.end(); compIter > frontIter; ) { /* move along the games from last to first */
            /* checkForSymmetry compares *frontIter to all symmetries of *compIter */
            if (checkForSymmetry(*frontIter, *compIter)) {
                compIter--;
                aMove.erase(compIter + 1);
            }
            else {
                compIter--;
            }
        } /* reset iterators for next loop */
        compIter = aMove.end();
        rearIter = aMove.end();
    }
}

【问题讨论】:

  • 你能举一个产生错误的简单例子吗?
  • 你能解释一下比较逻辑吗?如果您可以创建一个总订单,您可能会在一次通过中对向量进行排序并删除重复项。这将比您目前拥有的算法更快。
  • 值得一提的是std::vector::erase() 将迭代器返回到您刚刚删除的元素之后的下一个元素。这意味着您可以写入compIter = aMove.erase(compIter); 并将--compIter 移动到for() 作为递减步骤。
  • 错误示例 - 在 tictactoe 中有 3 种可能的第一次移动:中心、角落、侧面。在检查对称性之前,有 24 个第二步。对称性检查后,它们应该是 12,但我想出了 11 并验证了一个被错误地移动了。后来的 cmets 证实了我对迭代器的怀疑......
  • 我尝试了compIter = aMove.erase(compIter);,但这导致了无限循环。 .erase 下方的 else 条件也使得 for 循环内的 --compIter 不起作用。

标签: c++ vector iterator


【解决方案1】:

看起来至少有一个或两个您可以访问end 的位置超出了容器的末尾。我建议不要尝试解决有些复杂的逻辑,而是建议以下之一:

如果您可以创建始终相邻排列对称解决方案的排序,则可以应用该顺序,然后使用带有谓词的std::unique 来删除重复项。

如果你不能这样做,那么使用remove_if 而不是你复杂的内部循环:

void compareAllGames(move& aMove) { /* aMove is a vector of games. games are a struct of data */
    vector<game>:: iterator frontIter = aMove.begin();
    for (; frontIter < aMove.end() - 1; frontIter++) { /* move along the games from first to last */
        aMove.erase(std::remove_if(frontIter + 1, aMove.end(), std::bind1st(checkForSymmetry, *frontIter)), aMove.end());
    }
}

【讨论】:

  • 感谢您的评论。我在另一个上下文中读到了 remove_if,但不确定它在这里是否有意义。我还没有机会深入研究 STL 中的大部分算法。
  • remove_if 函数在这里可能是更好的选择。我检查对称性的方法是将第一个游戏和比较游戏传递给 checkSymmetry 函数,该函数将比较游戏转换为所有不同的对称性并与第一个游戏进行比较。我在http://www.cplusplus.com/ 找到了remove_if 的示例。我无法确定 remove_if 是否实际上从示例中的数组中删除了奇数项。有人可以确认吗?数组到底发生了什么?
【解决方案2】:

这并不是一个真正的答案,只是为了帮助向 OP 澄清我在评论中的意思。

/* aMove is a vector of games. games are a struct of data */
void compareAllGames( move &aMove ) 
{
    typedef vector<game>::iterator game_it;
    /* move along the games from first to last */
    for( game_it frontIter = aMove.begin(); frontIter != aMove.end(); ++frontIter ) 
    {
        /* move along the games from last to first */
        for( game_it compIter = aMove.end(); compIter != frontIter; --compIter )  
        {
            /* checkForSymmetry compares *frontIter to all symmetries of *compIter */
            if( checkForSymmetry( *frontIter, *compIter ) )
            {
                compIter = aMove.erase( compIter );
            }

        }
    }
}

【讨论】:

  • 我喜欢在这里使用 typedef。否则创建迭代器有点麻烦。
【解决方案3】:
for (; frontIter < rearIter; frontIter++) { /* move along the games from first to last */

你是对的:你应该使用!= 而不是&lt;。它可能没有任何区别,但没有理由不这样做,您通常也希望使用前增量而不是后增量,给出:

for (;frontIter != readiter; ++frontIter)
    for (compIter = aMove.end(); compIter > frontIter; ) { /* move along the games from last to first */

要反向遍历集合,您通常想要使用 reverse_iterator:

    vector<game>::reverse_iterator compIter;
    for (compIter=aMove.rbegin(); compIter != frontIter; ++compIter);

不过,我不记得您是否可以直接将迭代器与 reverse_iterator 进行比较——您可能需要将 fronIter 转换为 reverse_iterator 才能进行比较。

        /* checkForSymmetry compares *frontIter to all symmetries of *compIter */
        if (checkForSymmetry(*frontIter, *compIter)) {
            compIter--;
            aMove.erase(compIter + 1);
        }
        else {
            compIter--;
        }
    } /* reset iterators for next loop */

虽然转换不会完全简单,但看起来这最终是 std::remove_if 的变体,因此您可以将其更改为使用标准算法。

【讨论】:

  • 我最终简化了我的代码,避免了令人困惑的内部循环。最初我确实尝试使用reverse_iterator,但发现不同类型的迭代器无法相互比较。
【解决方案4】:

感谢所有 cmets。最后,我简化了具有两个令人困惑的循环的函数,并确定不是将所有游戏添加到移动然后检查重复/对称性,而是添加单个游戏并将其与当前列表进行比较。这有两个优点:易于理解的更简单的编码和更少的比较次数。这段代码实际上也可以工作,这并没有什么坏处。这些变化让我在对称函数中发现了几个错误。

这是我使用的最后一个函数:

bool compareAllGames(move& aMove) {
    vector<game>:: iterator frontIter = aMove.begin();
    vector<game>:: iterator rearIter = aMove.end() - 1;

    for (; frontIter != rearIter; ++frontIter) { // move along the games from first to last
        if (checkForSymmetry(*frontIter, aMove.back())) {
            aMove.pop_back();
            return true;
        }
    }
    return false;
}

现在我只需将我的代码重新添加到获胜者帐户中,我将为井字游戏树调整适当的计数。未调整的计数(忽略获胜者)为:1、2、12、38、108、174、228、174、89 和 23。

【讨论】:

    猜你喜欢
    • 2021-12-20
    • 1970-01-01
    • 2011-04-24
    • 1970-01-01
    • 2017-08-29
    • 2021-02-24
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    相关资源
    最近更新 更多