【问题标题】:Simple way to check a large number of similar conditions?检查大量类似条件的简单方法?
【发布时间】:2015-10-27 04:38:09
【问题描述】:

我正在开发一款游戏,我发现自己经常检查某些数量是否在代表我的世界的向量所接受的索引范围内:

if(a >= 0 && a < 16 && b >= 0 && b < 16 && c >= 0 && c < 16 && 
   d >= 0 && d < 16 && e >= 0 && e < 16)
{
    //do things with vector[a][b][c][d][e]
} 

我经常需要检查比这更多的条件。有没有办法让这些检查更简洁和/或更易于阅读?

或者,有没有办法可以完全避免进行检查?向量为16x16x16x16x16;我可以这样做吗,如果我给它一个 16 作为索引,它除了段错误什么都不做?

【问题讨论】:

  • 写一个inRange()函数并在所有变量上调用它?
  • 这是一个 x-y 问题。最好将 4 个点和 4 个键组合成 QRect 之类的东西,并使用 bool QRect::contains(const QPoint &amp; point, bool proper = false) const doc.qt.io/qt-5/qrect.html#contains 之类的方法
  • 首先,如果索引超出范围,我认为“什么都不做而不是段错误”是不明智的。原因很简单,调用代码可能假设它有一个正确的索引,这意味着错误会通过程序传播。但是,如果这无关紧要,您可以将 16x16x16x16x16 向量替换为单个向量,并使用位移位计算索引,这相对较快并且避免了跳过数组的开销。顺便说一句,如果大小 16 是固定的,那么 i != (i&amp;15) 中的任何 i 都是无效的。最后,可以选择使用vector::at()
  • 为了检查 0 和上限,如果您将类型转换为无符号值,负数会变得非常大。这使您可以通过一次比较来检查两个边界。
  • @KaiSchmidt 这些是一回事。坐标 (a,b,c,d,e) 指定五维空间中的一个点,您想知道该点是否在由这些变量的所有有效组合组成的五维矩形内。您缺少的见解是 a、b、c、d 和 e 的所有有效组合的共同点是它们表示特定矩形内的一个点,所有无效组合都在外部。

标签: c++


【解决方案1】:

您可以将变量作为元素存储在 std::vector 中,而不是像这样单独的变量:

bool test(const std::vector<int>& values)
{
    for(auto v: values)
        if(v < 0 || v >= 16)
            return false;
    return true;
}

或者,如果您使用C++11 或更高版本,您可以使用std::all_of

if(std::all_of(std::begin(values), std::end(values),
    [](int i){ return i >= 0 && i < 16; }))
{
    // do stuff with values
}

在这种情况下,您也可以使用std::array

【讨论】:

    【解决方案2】:

    您可以将构成索引的 5 个整数组合成一个 std::array 或您自己的类。

    using Index5 = std::array<int, 5>;
    

    然后你可以写一个类似的函数:

    bool contains(Index5 bounds, Index5 point) {
        for (Index5::size_type d = 0; d != bounds.size(); ++d) {
            if ((unsigned)point[d] > bounds[d])  // using the trick mentioned in comments
                return false;
        }
        return true;
    }
    

    然后像这样使用它:

    auto bounds = Index5{16, 16, 16, 16, 16};
    auto point = Index5{a, b, c, d, e};
    
    if (contains(bounds, point)) {
        // do things with point
    }
    

    一般来说,我建议使用Index5 之类的东西,而不是管理五个整数。

    【讨论】:

      【解决方案3】:

      如果数量 abcde 发生 经常在一起,所有人都需要保持在界限内 您的“世界”(例如,它们代表该世界中某物的“状态”) 那么定义一个主要目的是 保持由这五个数量组成的一个“状态”。

      然后确保是否有任何代码尝试将值存储在对象中 不在界限之内的那个类,一些合理的东西 (不是段错误)反而发生了, 并且没有越界值存储在那里。 这样,该类的对象可以安全地传递给任何 要求abcde 在范围内, 并且不需要任何此类功能进行边界检查 关于这五个值。

      【讨论】:

        【解决方案4】:

        这是一种紧凑而有效的检查方法。它采用二进制补码算法。

        bool IsInBounds(int a, int b, int c, int d, int e)
        {
            // Make sure only bits 0-3 are set (i.e. all values are 0-15)
            return ((a | b | c | d | e) & ~0xf) == 0;
        }
        

        这通过注意所有在 0-15 范围之外的值都有一个不是四个最低有效位之一的位集,并且范围内的所有值都没有。

        当然,只有在效率的提升超过代码可读性的损失时,才值得使用这种优化。

        【讨论】:

        • @mucaho : 哪台电脑没有补码算法?
        • @rkosegi 根据这个SE question几乎没有@
        【解决方案5】:

        你可以写一个可变参数check 函数:

        bool check(int a) {
          return 0 <= a && a < 16;
        }   
        
        template<typename... Args>
        bool check(int a, Args... args) {
          return check(a) && check(args...);
        }
        

        您可以像check(a, b, c, d, e, ...) 一样使用它。它还有一个优点是可以接受任意数量的条件。

        Here's a demo

        【讨论】:

        • 使用这种方式可以进一步优化比较Fastest way in C to determine if an integer is between two integers (inclusive) with known sets of valuesreturn ((unsigned)a &lt;= 16) &amp;&amp; check(args...);
        • 您可以像这样编写第二个函数来删除一些重复:return check(a) &amp;&amp; check(args...);
        • @LưuVĩnhPhúc 这确实是一个非常好的性能优化。这就是为什么编译器编写者已经应用它很长一段时间了,而且你不必混淆你的代码。
        • @Voo 我只是想知道为什么另一个问题中的 iOS 编译器没有实现这个
        • @LưuVĩnhPhúc 大概是因为在这种情况下涉及三个变量,编译器无法证明upper &gt;= lower。常量更容易。
        【解决方案6】:

        功能的重点是可重用性。如果您发现自己重复编写某些较长的表达式或语句组,则可能是时候将其重构了。

        在这种情况下,我会编写一个简单的函数来进行边界检查:

        bool isInBounds(int a, int b, int c, int d, int e)
        {
             return a >= 0 && a < 16 &&
                    b >= 0 && b < 16 &&
                    c >= 0 && c < 16 && 
                    d >= 0 && d < 16 &&
                    e >= 0 && e < 16;
        }
        

        然后用它代替你的长条件:

        if (isInBounds(a, b, c, d, e))
        {
            // do things with array[a][b][c][d][e]
        }
        

        【讨论】:

        • 当检查变量是否在某个范围内时,如果将条件写成 0
        • @RichSmith 这是一个很好的建议,尽管我个人总是喜欢先看到变量名,然后再看到常量。这确实是意见,所以无论人们想使用什么。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-18
        • 1970-01-01
        • 1970-01-01
        • 2011-03-06
        • 1970-01-01
        相关资源
        最近更新 更多