出于学习目的,我问“任何类型”,但实际上我现在正在处理一个
byte[,]
类型数组,它被称为 byteTable
,其内容如下所示:
00 01 E1 03 04 05
06 07 (08) 09 0A 0B
08 0D 0E 0F 10 (08)
12 13 14 15 04 17
(08) 19 {1A} 1B 1C 1D
1E 1F 20 10 (08) 23
括号只是为了便于阅读。我想找到数组中唯一的
1A
字节的索引,然后找到从 08
垂直向上方向最近的非唯一 1A
字节值并获取其距离。之后,我需要将中间的所有字节替换为FF
字节,包括1A
字节但不包括08
字节,然后从0E
所在的字节顺时针重复此操作。
或者,可以不求距离,而是覆盖所有字节,直到从
08
垂直开始顺时针移动,直到到达 1A
字节。哪种方法更可行?
结果应该是这样的:
00 FF E1 03 04 05
06 FF 08 09 0A 0B
08 FF FF FF FF 08
12 FF FF 15 FF 17
08 FF FF FF FF 1D
1E 1F 20 10 08 23
既然你正在学习,我不会给你完整的解决方案,而是给你一些提示。
您可以使用两个嵌套的 for 循环来查找 1A 值。一个循环在 x 上循环,另一个在 y 上循环。在单独的函数中实现它更容易,因为这样您可以简单地使用
return
语句来停止循环并返回结果。
您可以使用元组返回 x 和 y 索引:
(int x, int y)
。
参见:元组类型(C# 参考)
您需要一种方法来定义四个基本方向。一种可能的方法是在像这样的元组数组中声明移动方向的 delta x 和 y 值(我正在向下定义正 y 方向。如果您以相反的方式执行此操作,则必须更改
dy
的符号):
// up right down left
(int dx, int dy)[] directions = [(0, -1), (1, 0), (0, 1), (-1, 0)];
然后您可以使用模运算符
%
循环方向来限制索引,如下所示
int direction = 0; // upwards
// In the main loop
(int dx, int dy) = directions[direction];
direction = (direction + 1) % 4;
您可以在主循环中使用此代码来确定方向,并使用嵌套内循环通过分别将
dx
和 dy
添加到 x
和 y
来执行直线移动。
当到达边界或 08 时,内部循环结束。
当到达取决于移动方向的边界时,主循环结束。例如。当 dx 为 -1 时,x == 0 是边框,当 dx = 1 时,x == width_of_the_array - 1 是边框,等等
当然,您可以使用冗长的布尔表达式或一系列 if 语句以经典方式确定此条件,或者您可以使用非常先进的技术,使用 Lambda 表达式。
为此,我们将通过代表边界条件的委托来扩展方向数组的元组:
(int dx, int dy, Func<int, int, bool> isBorder)[] directions = [
( 0, -1, (ix, iy) => iy <= 0),
( 1, 0, (ix, iy) => ix >= Width - 1),
( 0, 1, (ix, iy) => iy >= Height - 1),
(-1, 0, (ix, iy) => ix <= 0)
];
然后您可以使用
获取方向相关参数var (dx, dy, isBorder) = directions[direction];
然后您可以使用
if (isBorder(x, y)) { ... }
检查边界条件。
这意味着您不需要计算距离,只需循环直到达到条件。例如
while (true) { // endless inner loop
if (byteTable[x, y] == 0x08) {
break; // end the inner loop and let the outer loop turn around clockwise
}
byteTable[x, y] = 0xFF;
if (a border is reached) {
return; // end the algorithm and print out the solution.
}
}
您还询问是否允许任何数组元素类型。 C# 有所谓的泛型。请参阅:通用类和方法。
它们允许您用泛型类型替换真实类型。例如。您可以用通用方法包装您的解决方案:
public static void Solve<T>(T[,] table, T uniqueStartValue, T wayMark)
{
// where wayMark corresponds to the 08 byte
...
}
请注意,您不能将
a == b
应用于泛型类型。您必须使用 a.Equals(b)
比较两个值。
我承认我使用了很多非常先进的东西。但是您当然可以使用涉及更多代码的更简单的方法。您可以为四个基本方向中的每个方向编写四段不同的代码,而不是使用方向数组(带有元组和 lambda)。
此外,您不需要从一开始就找到所有问题的解决方案。小步前进并慢慢改进解决方案,直到一切正常。假设这是一个控制台应用程序,
PrintTable(byteTable)
方法非常有用,不仅可以打印最终结果,还可以调试以打印循环内的中间状态,以便您可以观察算法如何进行(以及哪里失败) .
首先,您必须找到 1A 字节的 2d 索引。然后,您需要迭代索引,直到找到 08 字节,转动它,然后重复,直到到达数组的边缘。
public static class ArrayExtensions
{
// Find index of an element in a 2d array
public static (int x, int y) CoordinateOf<T>(this T[,] array, T value)
{
for (int x = 0; x < array.GetLength(0); ++x)
{
for (int y = 0; y < array.GetLength(1); ++y)
{
if (array[x, y]?.Equals(value) == true)
return (x, y);
}
}
return (-1, -1);
}
// Do the Stuff op asked
public static void DoStuff(this byte[,] array)
{
// Declare variables
var pos = array.CoordinateOf<byte>(0x1A);
var dir = (x: -1, y: 0); // Because 2d arrays are weird, this is 'up'
var width = array.GetLength(0);
var height = array.GetLength(1);
// Move the position an initial step
pos.x += dir.x;
pos.y += dir.y;
// Cache the starting position
var start = pos;
// Loop until we cross the edge of the array
while (pos.x >= 0 && pos.x < width
&& pos.y >= 0 && pos.y < height)
{
if (array[pos.x, pos.y] == 0x08)
{
// If the element is 0x08, turn clockwise
pos.x -= dir.x;
pos.y -= dir.y;
dir = (dir.y, -dir.x);
}
else
array[pos.x, pos.y] = 0xFF; // Set 0xFF
// Add direction to position
pos.x += dir.x;
pos.y += dir.y;
if (pos.x == start.x && dir.x == 0
&& pos.y == start.y && dir.y == -1)
break; // prevent infinite loop
}
}
}