什么是实现此 if else 逻辑的更好方法?
What would be a better way to implement this if else logic?
老实说,我想不出更好的标题,因为这是一个 scenario-based 问题:
我们有一个 Battleships 游戏,我们想要求玩家提供一对坐标来将他们的一艘船设置在网格上(更具体地说,是船的起点和终点)。假设正确给出了坐标并且船舶在网格平面的边界内,我们只需要检查船舶是否与当前在网格平面上的任何其他船舶发生碰撞。
上下文:网格有一个内容 属性,它是 ShipContent 或 EmptyContent。
CollisionChecker() 方法循环遍历先前给定的一对坐标之间的 space(请注意,这些坐标不能是对角线,这也假定已事先检查过)。
玩家想把自己的战舰放在A1和A4之间。由于字母相等,我们从 1 循环到 4,非常简单。但是,玩家可以根据他们的命令分别输入 A4 和 A1。这两种情况在逻辑上都是合理的,但它们可能会导致 OutOfBound 异常 and/or 不正确的循环,如果它们没有得到相应的处理。
最后一点上下文,CoordinateLetter 是一个包含整个英文字母表的枚举。
static bool CollisionChecker(Grid[][] gridPlane, CoordinateLetter coordinateLetter1, CoordinateLetter coordinateLetter2, int coordinateNumber1, int coordinateNumber2)
{
bool lettersEqual = coordinateLetter1 == coordinateLetter2;
bool cL1Bigger = coordinateLetter1 > coordinateLetter2;
bool cN1Bigger = coordinateNumber1 > coordinateNumber2;
if (lettersEqual && cN1Bigger)
for (int num = coordinateNumber2; num <= coordinateNumber1; num++)
{
// Assume that if it collides, it returns false
}
else if (lettersEqual && !cN1Bigger)
{
for (int num = coordinateNumber1; num <= coordinateNumber2; num++)
{
// Assume that if it collides, it returns false
}
}
else if (!lettersEqual && cL1Bigger)
{
for (int num = (int)coordinateLetter2; num <= (int)coordinateLetter1; num++)
{
// Assume that if it collides, it returns false
}
}
else
{
for (int num = (int)coordinateLetter1; num <= (int)coordinateLetter2; num++)
{
// Assume that if it collides, it returns false
}
}
return true;
}
这段代码让我脊背发凉。我不喜欢链接 if else 是这样的。什么是更好的实现方式?
您可以使代码更易于遵循和维护,也许在命名方面更合乎逻辑。
但有时你会做你必须做的事情。
这只是我在胡闹...
static bool CollisionChecker(Grid[][] gridPlane, CoordinateLetter gridRow1, CoordinateLetter gridRow2, int gridCol1, int gridCol2)
{
bool sameRow = (gridRow1 == gridRow2);
if (!(sameRow || (gridCol1 == gridCol2)))
throw new ArgumentException("Diagonal not allowed!");
if (sameRow)
{
int row = (int)gridRow1;
int start = Math.Min(gridCol1, gridCol2);
int end = Math.Max(gridCol1, gridCol2);
for (int i = start; i <= end; i++)
{
//Check gridPlane[row][i]
}
}
else //Same column
{
int col = gridCol1;
int start = Math.Min((int)gridRow1, (int)gridRow2);
int end = Math.Max((int)gridRow1, (int)gridRow2);
for (int i = start; i <= end; i++)
{
//Check gridPlane[i][col]
}
}
return true;
}
你在这里真正做的是在两个向量之间进行插值,所以你不需要明确地处理每一种情况,你可以只使用它们的归一化差异来遍历网格。让我解释得更好。
首先,让我们用数字替换坐标系中的字母(只是为了让事情更简单),以便 A1 = (1; 1)、B4 = (2; 4) 等。想象一下,您正试图从 E1 转到 A1,或从 X = (5; 1) 转到 Y = (1; 1)。要一次完成这个旅程,您需要从 X 的第一个分量中删除 4 个单位,这在数学上可以写为 X + (-4; 0) = Y。重新排列这给了我们找到任何之间的差异的公式两个向量:Y - X = (-4; 0),也就是说,只需从你的目标位置 (Y) 中减去你的起始位置 (X)。
但您不想一口气走完这段旅程,您想分步进行,以检查是否有另一艘船挡住了您。现在,因为你在一个离散的整数网格中(即你在位置 1 或 2,你不能在位置 1.5),这简化了事情:你可以采取的最长步骤,同时不会错过沿途的任何船只way 的长度为 1。因此,如果您的点之间的差异是 (-4; 0),则您需要 (-1; 0) 的步长。如果差异为 (0; 5),则您需要 (0; 1) 的步长。这种将一个向量减为长度为 1 同时保持其方向的过程称为 归一化 。由于您的矢量将始终与轴对齐(即您不会沿对角线行走),因此您可以作弊并在每个组件上使用 Math.sign 而不是进行“完整”矢量归一化。
把它们放在一起给你这样的东西(考虑这个伪代码,我什至不知道它是否会编译):
int diffX = (int)coordinateLetter2 - (int)coordinateLetter1;
int diffY = coordinateNumber2 - coordinateNumber1;
int stepX = Math.sign(diffX);
int stepY = Math.sign(diffY);
CoordinateLetter currentX = coordinateLetter1;
int currentY = coordinateNumber1;
while (currentX != coordinateLetter2 || currentY != coordinateNumber2)
{
if (HasCollision(currentX, currentY))
return false;
currentX += stepX;
currentY += stepY;
}
return true;
我还建议创建一个结构来保存位置的两个坐标(字母和数字),这样您就可以直接对位置而不是其组件进行操作。这使得二维码的推理变得更容易。
老实说,我想不出更好的标题,因为这是一个 scenario-based 问题:
我们有一个 Battleships 游戏,我们想要求玩家提供一对坐标来将他们的一艘船设置在网格上(更具体地说,是船的起点和终点)。假设正确给出了坐标并且船舶在网格平面的边界内,我们只需要检查船舶是否与当前在网格平面上的任何其他船舶发生碰撞。
上下文:网格有一个内容 属性,它是 ShipContent 或 EmptyContent。
CollisionChecker() 方法循环遍历先前给定的一对坐标之间的 space(请注意,这些坐标不能是对角线,这也假定已事先检查过)。
玩家想把自己的战舰放在A1和A4之间。由于字母相等,我们从 1 循环到 4,非常简单。但是,玩家可以根据他们的命令分别输入 A4 和 A1。这两种情况在逻辑上都是合理的,但它们可能会导致 OutOfBound 异常 and/or 不正确的循环,如果它们没有得到相应的处理。
最后一点上下文,CoordinateLetter 是一个包含整个英文字母表的枚举。
static bool CollisionChecker(Grid[][] gridPlane, CoordinateLetter coordinateLetter1, CoordinateLetter coordinateLetter2, int coordinateNumber1, int coordinateNumber2)
{
bool lettersEqual = coordinateLetter1 == coordinateLetter2;
bool cL1Bigger = coordinateLetter1 > coordinateLetter2;
bool cN1Bigger = coordinateNumber1 > coordinateNumber2;
if (lettersEqual && cN1Bigger)
for (int num = coordinateNumber2; num <= coordinateNumber1; num++)
{
// Assume that if it collides, it returns false
}
else if (lettersEqual && !cN1Bigger)
{
for (int num = coordinateNumber1; num <= coordinateNumber2; num++)
{
// Assume that if it collides, it returns false
}
}
else if (!lettersEqual && cL1Bigger)
{
for (int num = (int)coordinateLetter2; num <= (int)coordinateLetter1; num++)
{
// Assume that if it collides, it returns false
}
}
else
{
for (int num = (int)coordinateLetter1; num <= (int)coordinateLetter2; num++)
{
// Assume that if it collides, it returns false
}
}
return true;
}
这段代码让我脊背发凉。我不喜欢链接 if else 是这样的。什么是更好的实现方式?
您可以使代码更易于遵循和维护,也许在命名方面更合乎逻辑。
但有时你会做你必须做的事情。
这只是我在胡闹...
static bool CollisionChecker(Grid[][] gridPlane, CoordinateLetter gridRow1, CoordinateLetter gridRow2, int gridCol1, int gridCol2)
{
bool sameRow = (gridRow1 == gridRow2);
if (!(sameRow || (gridCol1 == gridCol2)))
throw new ArgumentException("Diagonal not allowed!");
if (sameRow)
{
int row = (int)gridRow1;
int start = Math.Min(gridCol1, gridCol2);
int end = Math.Max(gridCol1, gridCol2);
for (int i = start; i <= end; i++)
{
//Check gridPlane[row][i]
}
}
else //Same column
{
int col = gridCol1;
int start = Math.Min((int)gridRow1, (int)gridRow2);
int end = Math.Max((int)gridRow1, (int)gridRow2);
for (int i = start; i <= end; i++)
{
//Check gridPlane[i][col]
}
}
return true;
}
你在这里真正做的是在两个向量之间进行插值,所以你不需要明确地处理每一种情况,你可以只使用它们的归一化差异来遍历网格。让我解释得更好。
首先,让我们用数字替换坐标系中的字母(只是为了让事情更简单),以便 A1 = (1; 1)、B4 = (2; 4) 等。想象一下,您正试图从 E1 转到 A1,或从 X = (5; 1) 转到 Y = (1; 1)。要一次完成这个旅程,您需要从 X 的第一个分量中删除 4 个单位,这在数学上可以写为 X + (-4; 0) = Y。重新排列这给了我们找到任何之间的差异的公式两个向量:Y - X = (-4; 0),也就是说,只需从你的目标位置 (Y) 中减去你的起始位置 (X)。
但您不想一口气走完这段旅程,您想分步进行,以检查是否有另一艘船挡住了您。现在,因为你在一个离散的整数网格中(即你在位置 1 或 2,你不能在位置 1.5),这简化了事情:你可以采取的最长步骤,同时不会错过沿途的任何船只way 的长度为 1。因此,如果您的点之间的差异是 (-4; 0),则您需要 (-1; 0) 的步长。如果差异为 (0; 5),则您需要 (0; 1) 的步长。这种将一个向量减为长度为 1 同时保持其方向的过程称为 归一化 。由于您的矢量将始终与轴对齐(即您不会沿对角线行走),因此您可以作弊并在每个组件上使用 Math.sign 而不是进行“完整”矢量归一化。
把它们放在一起给你这样的东西(考虑这个伪代码,我什至不知道它是否会编译):
int diffX = (int)coordinateLetter2 - (int)coordinateLetter1;
int diffY = coordinateNumber2 - coordinateNumber1;
int stepX = Math.sign(diffX);
int stepY = Math.sign(diffY);
CoordinateLetter currentX = coordinateLetter1;
int currentY = coordinateNumber1;
while (currentX != coordinateLetter2 || currentY != coordinateNumber2)
{
if (HasCollision(currentX, currentY))
return false;
currentX += stepX;
currentY += stepY;
}
return true;
我还建议创建一个结构来保存位置的两个坐标(字母和数字),这样您就可以直接对位置而不是其组件进行操作。这使得二维码的推理变得更容易。