C#中带有“包边”的二维数组

时间:2011-11-17 04:24:36

标签: c# arrays multidimensional-array subclass

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

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

var arr = new int[10,10];

访问索引超出0-9范围的元素是错误的。在某些应用中(例如,某些2D阵列代表世界的游戏),必须“包裹”阵列的边缘。例如,

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++)是表达“从零到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的问题,并获得了一些good answers

2 个答案:

答案 0 :(得分:6)

您可以做的一件事就是过载[,]

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 oughta。


其他东西:

  • 您的state变量应该是私有的。
  • 除非您绝对需要整体,否则请考虑使用通用的state数组类型。您的Grid课程变为Grid<T>
  • 如果括号[,]超载,您可以摆脱atset个功能。
  • 至于你的世界级,有一个简化的网格,要问的问题是这个经典:是或有?你的世界是一个网格还是它有一个网格?只有你能回答这个问题,但我倾向于HAS。
  • 考虑一个Grid构造函数,它将现成的2d数组作为参数:

示例:

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) =&gt; 0
  • mod(1,10) =&gt; 1
  • mod(-1,10) =&gt; 9
  • mod(10,10) =&gt; 0
  • mod(-10,10) =&gt; 0
  • mod(11,10) =&gt; 1
  • mod(-11,10) =&gt; 9

答案 1 :(得分:-1)

只需使用模%函数访问数组。对于NM数组,请使用以下内容。

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

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