具有“包裹边缘”的二维阵列在 C# 中

发布于 2024-12-15 23:05:58 字数 2823 浏览 3 评论 0原文

警告:我是 C# 新手。除了回答我的问题之外,如果您在看到我的代码后有任何一般性的提示,欢迎您提出。

假设我在 C# 中定义了一个大小为 10x10 的二维数组:

var arr = new int[10,10];

访问索引超出 0-9 范围的元素是错误的。在某些应用程序中(例如,一些二维数组代表一个世界的游戏),有必要“包裹”数组的边缘。例如,

arr[-1, 0]

实际上会引用 arr[9,0] 处的元素。

我一直在使用的一种方法是下面的课程。我没有子类化 System.Array 因为 C# 显然禁止这样做。

用法示例:

var grid = new Grid(10,10);
grid.set(-1, 0, 100); // Set element at (-1,0) to value 100.
grid.at(-1,0); // retrieve element at (-1,0)

类本身:

class Grid
{
    public int[,] state;

    public int width { get { return state.GetLength(0); } }
    public int height { get { return state.GetLength(1); } }

    public Grid(int width_init, int height_init)
    {
        state = new int[width_init, height_init];
    }

    int mod(int a, int b)
    {
        if (a >= 0)
            return a % b;
        else
            return (b + a % b) % b;
    }

    int wrap_x(int x) { return mod(x, width); }
    int wrap_y(int y) { return mod(y, height); }

    public int at(int x, int y)
    {
        return state[wrap_x(x), wrap_y(y)];
    }

    public void set(int x, int y, int val)
    {
        state[wrap_x(x), wrap_y(y)] = val;
    }    

    // more stuff here...
}

问题:是否有提供此类类的游戏/创意编码框架?

问题:你能想到一个我可以在上面使用的更简单的mod吗?

为了处理每个元素以及相应的“x”和“y”,我使用以下方法:

public void each(Action<int, int, int> proc)
{
    for (int x = 0; x < width; x++)
        for (int y = 0; y < height; y++)
            proc(x, y, state[x, y]);
}

问题:我四处寻找在 System.Array 上定义的类似方法,但我没有找到一个。我错过了吗?

问题:在上面的代码中,for(int x = 0; x < width; x++)是表达“从0到N乘1”的常见习语。有没有一种机制可以在 C# 中表达这一点?即我想将上面的内容写为:

width.up_to((x) =>
    height.up_to((y) =>
        proc(x, y, state[x, y]);

其中 up_to 是整数方法。是否已经定义了类似 up_to 的内容?

与Scheme 中的map 类似,这里有一个map 方法,它将Func 应用于每个元素及其相应的索引。它返回一个新的Grid

public Grid map(Func<int, int, int, int> proc)
{
    var grid = new Grid(width, height);
    each((x, y, val) => grid.state[x, y] = proc(x, y, val));
    return grid;
}

问题:假设我设置了一个子类class World : Grid,它添加了额外的实例变量。上述 map 的问题在于,当在 World 的实例上调用时,您会得到一个 Grid,而不是 World代码>.我应该如何解决这个问题?这是完全错误的做法吗?也许更好的设计是不子类化Grid,而是将其保留为World中的实例变量。

抱歉提交时间太长。 :-)

更新: 我分别询问了有关 upto 的问题,并得到了一些好的答案

Warning: I'm a C# newb. Besides answering my questions, if you have any tips in general after seeing my code, they are welcome.

Let's say I define a two-dimensional array of size 10x10 in C#:

var arr = new int[10,10];

It is an error to access elements with indices out of the range 0-9. In some applications, (e.g. some games where the 2d-array represents a world) it's necessary to "wrap" the edges of the array. So for example

arr[-1, 0]

would actually refer to the element at arr[9,0].

One approach I've been using is the following class. I didn't subclass System.Array because C# apparently forbids doing so.

Example usage:

var grid = new Grid(10,10);
grid.set(-1, 0, 100); // Set element at (-1,0) to value 100.
grid.at(-1,0); // retrieve element at (-1,0)

The class itself:

class Grid
{
    public int[,] state;

    public int width { get { return state.GetLength(0); } }
    public int height { get { return state.GetLength(1); } }

    public Grid(int width_init, int height_init)
    {
        state = new int[width_init, height_init];
    }

    int mod(int a, int b)
    {
        if (a >= 0)
            return a % b;
        else
            return (b + a % b) % b;
    }

    int wrap_x(int x) { return mod(x, width); }
    int wrap_y(int y) { return mod(y, height); }

    public int at(int x, int y)
    {
        return state[wrap_x(x), wrap_y(y)];
    }

    public void set(int x, int y, int val)
    {
        state[wrap_x(x), wrap_y(y)] = val;
    }    

    // more stuff here...
}

Question: Is there a game/creative-coding framework out there that provides this sort of class?

Question: Can you think of a simpler mod I can use in the above?

In order to process each element along with the corresponding "x" and "y", I use the following method:

public void each(Action<int, int, int> proc)
{
    for (int x = 0; x < width; x++)
        for (int y = 0; y < height; y++)
            proc(x, y, state[x, y]);
}

Question: I looked around for a similar method defined on System.Array but I didn't find one. Did I miss it?

Question: In the above, for(int x = 0; x < width; x++) is the common idiom expressing "go from zero up to N by 1". Is there a mechanism which expresses this in C#? I.e. I'd like to write the above as:

width.up_to((x) =>
    height.up_to((y) =>
        proc(x, y, state[x, y]);

where up_to would be a method on integers. Is there something like up_to already defined?

Similar to map from Scheme, here's a map method which applies a Func to each element and its corresponding indices. It returns a new Grid.

public Grid map(Func<int, int, int, int> proc)
{
    var grid = new Grid(width, height);
    each((x, y, val) => grid.state[x, y] = proc(x, y, val));
    return grid;
}

Question: Let's suppose I setup a subclass class World : Grid which adds additional instance variables. The trouble with the above map is that when called on an instance of World, you get a Grid, not a World. How should I fix this? Is this the wrong approach altogether? Perhaps a better design is to not subclass Grid but to have keep it as an instance variable in World.

Sorry for the long submission. :-)

Update: I asked the question about upto separately and got some good answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

奢欲 2024-12-22 23:05:58

为了方便对网格的引用,您可以做的一件事是重载 [,]

public int this[int x, int y]
{
    get { return state[wrap_x(x), wrap_y(y)]; }
    set { state[wrap_x(x), wrap_y(y)] = value; }
}

如果您发现语法更合适,请使用它。

关于您的 mod 函数,我能提出的最佳建议是使这些变量(a 和 b)有意义。 indexmaxSize 应该这样做。


其他内容:

  • 您的 state 变量应该是私有的。
  • 除非您绝对需要整数,否则请考虑使用泛型作为 state 数组的类型。您的 Grid 类将变为 Grid
  • 通过括号 [,] 重载,您可以摆脱 atset 函数。
  • 至于你的世界级,使用简化的网格,要问的问题是这个经典的问题:是还是有?你的世界是网格还是有网格?只有你可以回答这个问题,但我倾向于“HAS”。
  • 考虑一个 Grid 构造函数,它采用现成的二维数组作为参数:

示例:

public Grid(int[,] state)
{
    this.state = state;
}
  • 只需稍加修改即可使 mod 对任何值(多重环绕)有效。

示例:

int mod(int index, int maxSize)
{
    while (index < 0) index += maxSize;
    return index % maxSize;
}

结果:

  • mod(0,10) => 0
  • mod(1,10) =>; 1
  • mod(-1,10) =>; 9
  • mod(10,10) =>; 0
  • mod(-10,10) =>; 0
  • mod(11,10) =>; 1
  • mod(-11,10) =>; 9

One thing you can do to facilitate reference to your grid is overload [,]:

public int this[int x, int y]
{
    get { return state[wrap_x(x), wrap_y(y)]; }
    set { state[wrap_x(x), wrap_y(y)] = value; }
}

If you find the syntax more suitable, go for it.

Regarding your mod function, the best suggestion I can make is to make those variables (a and b) meaningful. index and maxSize oughta do it.


Other stuff:

  • Your state variable should be private.
  • Unless you absolutely, exclusively need ints, consider using a generic for the type of your state array. Your Grid class becomes Grid<T>.
  • With the bracket [,] overload, you can get rid of your at and set functions.
  • As for your World class, with a simplified Grid, the question to ask is this classic: is or has? IS your World a Grid or does it HAVE a Grid? Only you can answer that, but I'm leaning towards HAS.
  • Consider a Grid constructor which takes a ready-made 2d array as a parameter:

Example:

public Grid(int[,] state)
{
    this.state = state;
}
  • mod can be made valid for any value (multiple wrap around) with a slight modification.

Example:

int mod(int index, int maxSize)
{
    while (index < 0) index += maxSize;
    return index % maxSize;
}

Results:

  • mod(0,10) => 0
  • mod(1,10) => 1
  • mod(-1,10) => 9
  • mod(10,10) => 0
  • mod(-10,10) => 0
  • mod(11,10) => 1
  • mod(-11,10) => 9
倾其所爱 2024-12-22 23:05:58

只需使用 modulo % 函数访问数组即可。对于 N by M 数组,请使用以下内容。

int x = A[i % N, j % M];

它会完全满足您的需要。在您的示例中,使用 arr[-1 % 10, 0 % 10] 而不是 arr[-1,0]。不需要包装函数或额外的代码!

Just access the array with the modulo % function. For an N by M array use the following.

int x = A[i % N, j % M];

it will do exactly what you need. In your example use arr[-1 % 10, 0 % 10] instead of arr[-1,0]. There is no need for a wrapper function, or additional code!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文