Ora*_*kia 111 c++ if-statement
我有以下代码:
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;
Run Code Online (Sandbox Code Playgroud)
我想避开if
s链; 真的很难看.是否有另一种可能更清晰的写作方式?
Bor*_*der 176
#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;
}
Run Code Online (Sandbox Code Playgroud)
我就是这样做的.(根据我之前的评论).
Ste*_*mer 71
您可以map::lower_bound
在地图中使用和存储每个角度的上限.
下面的工作示例:
#include <cassert>
#include <map>
enum Direction
{
RIGHT,
UP_RIGHT,
UP,
UP_LEFT,
LEFT,
DOWN_LEFT,
DOWN,
DOWN_RIGHT
};
using AngleDirMap = std::map<int, Direction>;
AngleDirMap map = {
{ 30, RIGHT },
{ 60, UP_RIGHT },
{ 120, UP },
{ 150, UP_LEFT },
{ 210, LEFT },
{ 240, DOWN_LEFT },
{ 300, DOWN },
{ 330, DOWN_RIGHT },
{ 360, RIGHT }
};
Direction direction(int angle)
{
assert(angle >= 0 && angle <= 360);
auto it = map.lower_bound(angle);
return it->second;
}
int main()
{
Direction d;
d = direction(45);
assert(d == UP_RIGHT);
d = direction(30);
assert(d == RIGHT);
d = direction(360);
assert(d == RIGHT);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
dbu*_*ush 57
创建一个数组,其每个元素与一个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
};
Run Code Online (Sandbox Code Playgroud)
然后你可以用角度/ 30索引数组:
this->_car.edir = dirlist[(this->_car.getAbsoluteAngle() % 360) / 30];
Run Code Online (Sandbox Code Playgroud)
无需比较或分支.
然而,结果略微偏离原始.边界上的值,即30,60,120等,放在下一个类别中.例如,在原始代码中,有效值为UP_RIGHT
31到60.上面的代码分配30到59 UP_RIGHT
.
我们可以通过从角度减去1来解决这个问题:
this->_car.edir = dirlist[((this->_car.getAbsoluteAngle() - 1) % 360) / 30];
Run Code Online (Sandbox Code Playgroud)
现在,这给了我们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;
Run Code Online (Sandbox Code Playgroud)
mot*_*zzt 49
该代码并不丑陋,简单,实用,易读且易于理解.它将以它自己的方法被隔离,所以没有人必须在日常生活中处理它.并且万一有人必须检查它 - 可能是因为他正在调试你的应用程序以解决其他地方的问题 - 这很容易让他花两秒钟来理解代码及其作用.
如果我正在进行这样的调试,我很乐意花五分钟时间来尝试理解你的功能.在这方面,所有其他功能完全失败,因为它们改变了一个简单的,忘记它,无错误的例程,在一个复杂的混乱中,人们在调试时将被迫深入分析和测试.作为一名项目经理,我自己会因为开发人员采取简单的任务而不是将其实现为简单,无害的方式而感到不安,浪费时间将其实现为过于复杂的方式.只要想想你一直在思考它,然后来到SO询问,一切只是为了恶化维护和可读性.
也就是说,您的代码中存在一个常见错误,使其可读性降低,并且您可以轻松地进行一些改进:
int angle = this->_car.getAbsoluteAngle();
if (angle <= 30 || angle >= 330)
return Car::EDirection::RIGHT;
else if (angle <= 60)
return Car::EDirection::UP_RIGHT;
else if (angle <= 120)
return Car::EDirection::UP;
else if (angle <= 150)
return Car::EDirection::UP_LEFT;
else if (angle <= 210)
return Car::EDirection::LEFT;
else if (angle <= 240)
return Car::EDirection::DOWN_LEFT;
else if (angle <= 300)
return Car::EDirection::DOWN;
else if (angle <= 330)
return Car::EDirection::DOWN_RIGHT;
Run Code Online (Sandbox Code Playgroud)
把它放到一个方法中,将返回的值赋给对象,折叠方法,并在永恒的剩余时间内忘记它.
PS还有另一个错误超过330门槛,但我不知道你想怎么对待它,所以我根本没有解决它.
稍后更新
根据评论,如果有的话你甚至可以摆脱其他:
int angle = this->_car.getAbsoluteAngle();
if (angle <= 30 || angle >= 330)
return Car::EDirection::RIGHT;
if (angle <= 60)
return Car::EDirection::UP_RIGHT;
if (angle <= 120)
return Car::EDirection::UP;
if (angle <= 150)
return Car::EDirection::UP_LEFT;
if (angle <= 210)
return Car::EDirection::LEFT;
if (angle <= 240)
return Car::EDirection::DOWN_LEFT;
if (angle <= 300)
return Car::EDirection::DOWN;
if (angle <= 330)
return Car::EDirection::DOWN_RIGHT;
Run Code Online (Sandbox Code Playgroud)
我没有这样做,因为我觉得某一点只是一个自己的偏好问题,我的回答的范围是(并且是)对你对"代码的丑陋"的关注给出不同的观点.无论如何,正如我所说,有人在评论中指出了这一点,我认为展示它是有道理的.
Bij*_*ung 39
在伪代码中:
angle = (angle + 30) %360; // Offset by 30.
Run Code Online (Sandbox Code Playgroud)
因此,我们有0-60
,60-90
,90-150
,...作为类别.在每个90度的象限中,一个部分有60个,一个部分有30个.所以,现在:
i = angle / 90; // Figure out the quadrant. Could be 0, 1, 2, 3
j = (angle - 90 * i) >= 60? 1: 0; // In the quardrant is it perfect (eg: RIGHT) or imperfect (eg: UP_RIGHT)?
index = i * 2 + j;
Run Code Online (Sandbox Code Playgroud)
在包含适当顺序的枚举的数组中使用索引.
Cal*_*eth 18
switch (this->_car.getAbsoluteAngle() / 30) // integer division
{
case 0:
case 11: this->_car.edir = Car::EDirection::RIGHT; break;
case 1: this->_car.edir = Car::EDirection::UP_RIGHT; break;
...
case 10: this->_car.edir = Car::EDirection::DOWN_RIGHT; break;
}
Run Code Online (Sandbox Code Playgroud)
Ðаn*_*Ðаn 16
忽略你的第if
一个有点特殊情况,其余的都遵循完全相同的模式:最小值,最大值和方向; 伪代码:
if (angle > min && angle <= max)
_car.edir = direction;
Run Code Online (Sandbox Code Playgroud)
制作这个真正的C++可能看起来像:
enum class EDirection { NONE,
RIGHT, UP_RIGHT, UP, UP_LEFT, LEFT, DOWN_LEFT, DOWN, DOWN_RIGHT };
struct AngleRange
{
int min, max;
EDirection direction;
};
Run Code Online (Sandbox Code Playgroud)
现在,而不是写一堆if
s,只是循环你的各种可能性:
EDirection direction_from_angle(int angle, const std::vector<AngleRange>& angleRanges)
{
for (auto&& angleRange : angleRanges)
{
if ((angle > angleRange.min) && (angle <= angleRange.max))
return angleRange.direction;
}
return EDirection::NONE;
}
Run Code Online (Sandbox Code Playgroud)
(throw
荷兰国际集团的例外,而不是return
ING NONE
是另一种选择).
然后你会打电话给:
_car.edir = direction_from_angle(_car.getAbsoluteAngle(), {
{30, 60, EDirection::UP_RIGHT},
{60, 120, EDirection::UP},
// ... etc.
});
Run Code Online (Sandbox Code Playgroud)
这种技术称为数据驱动编程.除了摆脱一堆if
s之外,它还允许您轻松添加更多方向(例如,NNW)或减少数量(左,右,上,下)而无需重新编写其他代码.
(处理你的第一个特殊情况留作"读者练习".:-))
x4u*_*x4u 12
尽管基于查找表的所提出的变体angle / 30
可能是优选的,但是这里是使用硬编码二进制搜索来最小化比较次数的替代方案.
static Car::EDirection directionFromAngle( int angle )
{
if( angle <= 210 )
{
if( angle > 120 )
return angle > 150 ? Car::EDirection::LEFT
: Car::EDirection::UP_LEFT;
if( angle > 30 )
return angle > 60 ? Car::EDirection::UP
: Car::EDirection::UP_RIGHT;
}
else // > 210
{
if( angle <= 300 )
return angle > 240 ? Car::EDirection::DOWN
: Car::EDirection::DOWN_LEFT;
if( angle <= 330 )
return Car::EDirection::DOWN_RIGHT;
}
return Car::EDirection::RIGHT; // <= 30 || > 330
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
12813 次 |
最近记录: |