将标题分类为 8 个方向时如何避免 if / else if 链?

新手上路,请多包涵

我有以下代码:

 if (this->_car.getAbsoluteAngle() <= 30 || this->_car.getAbsoluteAngle() >= 330)
  this->_car.edir = Car::EDirection::RIGHT;
else if (this->_car.getAbsoluteAngle() > 30 && this->_car.getAbsoluteAngle() <= 60)
  this->_car.edir = Car::EDirection::UP_RIGHT;
else if (this->_car.getAbsoluteAngle() > 60 && this->_car.getAbsoluteAngle() <= 120)
  this->_car.edir = Car::EDirection::UP;
else if (this->_car.getAbsoluteAngle() > 120 && this->_car.getAbsoluteAngle() <= 150)
  this->_car.edir = Car::EDirection::UP_LEFT;
else if (this->_car.getAbsoluteAngle() > 150 && this->_car.getAbsoluteAngle() <= 210)
  this->_car.edir = Car::EDirection::LEFT;
else if (this->_car.getAbsoluteAngle() > 210 && this->_car.getAbsoluteAngle() <= 240)
  this->_car.edir = Car::EDirection::DOWN_LEFT;
else if (this->_car.getAbsoluteAngle() > 240 && this->_car.getAbsoluteAngle() <= 300)
  this->_car.edir = Car::EDirection::DOWN;
else if (this->_car.getAbsoluteAngle() > 300 && this->_car.getAbsoluteAngle() <= 330)
  this->_car.edir = Car::EDirection::DOWN_RIGHT;

我想避免 if s 链;真的很丑。是否有另一种可能更清洁的方式来写这个?

原文由 Oraekia 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 375
2 个回答
#include <iostream>

enum Direction { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT };

Direction GetDirectionForAngle(int angle)
{
    const Direction slices[] = { RIGHT, UP_RIGHT, UP, UP, UP_LEFT, LEFT, LEFT, DOWN_LEFT, DOWN, DOWN, DOWN_RIGHT, RIGHT };
    return slices[(((angle % 360) + 360) % 360) / 30];
}

int main()
{
    // This is just a test case that covers all the possible directions
    for (int i = 15; i < 360; i += 30)
        std::cout << GetDirectionForAngle(i) << ' ';

    return 0;
}

我就是这样做的。 (根据我之前的评论)。

原文由 Borgleader 发布,翻译遵循 CC BY-SA 3.0 许可协议

创建一个数组,其中的每个元素都与一个 30 度的块相关联:

 Car::EDirection dirlist[] = {
    Car::EDirection::RIGHT,
    Car::EDirection::UP_RIGHT,
    Car::EDirection::UP,
    Car::EDirection::UP,
    Car::EDirection::UP_LEFT,
    Car::EDirection::LEFT,
    Car::EDirection::LEFT,
    Car::EDirection::DOWN_LEFT,
    Car::EDirection::DOWN,
    Car::EDirection::DOWN,
    Car::EDirection::DOWN_RIGHT,
    Car::EDirection::RIGHT
};

然后您可以使用角度 / 30 对数组进行索引:

 this->_car.edir = dirlist[(this->_car.getAbsoluteAngle() % 360) / 30];

无需比较或分支。

然而,结果与原始结果 略有 不同。边界上的值,即 30、60、120 等,被放置在下一个类别中。例如,在原始代码中, UP_RIGHT 的有效值为 31 到 60。上述代码将 30 到 59 分配给 UP_RIGHT

我们可以通过从角度减去 1 来解决这个问题:

 this->_car.edir = dirlist[((this->_car.getAbsoluteAngle() - 1) % 360) / 30];

现在这给了我们 RIGHT 30, UP_RIGHT 60,等等。

在 0 的情况下,表达式变为 (-1 % 360) / 30 。这是有效的,因为 -1 % 360 == -1-1 / 30 == 0 ,所以我们仍然得到索引 0。

C++ 标准 的第 5.6 节确认了这种行为:

4 二进制 / 运算符产生商,二进制 % 运算符产生第一个表达式除以第二个表达式的余数。如果 /% 的第二个操作数为零,则行为未定义。对于整数操作数, / 运算符产生代数商,其中任何小数部分被丢弃。如果商 a/b 可以用结果类型表示,则 (a/b)*b + a%b 等于 a

编辑:

关于这样的结构的可读性和可维护性提出了许多问题。 motoDrizzt 给出的答案是简化原始构造的一个很好的例子,该构造更易于维护并且不是那么“丑陋”。

扩展他的答案,这是另一个使用三元运算符的示例。由于原始帖子中的每个案例都分配给相同的变量,因此使用此运算符可以帮助进一步提高可读性。

 int angle = ((this->_car.getAbsoluteAngle() % 360) + 360) % 360;

this->_car.edir = (angle <= 30)  ?  Car::EDirection::RIGHT :
                  (angle <= 60)  ?  Car::EDirection::UP_RIGHT :
                  (angle <= 120) ?  Car::EDirection::UP :
                  (angle <= 150) ?  Car::EDirection::UP_LEFT :
                  (angle <= 210) ?  Car::EDirection::LEFT :
                  (angle <= 240) ?  Car::EDirection::DOWN_LEFT :
                  (angle <= 300) ?  Car::EDirection::DOWN:
                  (angle <= 330) ?  Car::EDirection::DOWN_RIGHT :
                                    Car::EDirection::RIGHT;

原文由 dbush 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题