[英]Accessing multi-dimensional array with one-dimensional index
請參見下面的代碼。 用一維索引訪問多維數組的方式是什么。 Foreach可以做到。 是的,我知道,帶有收益的IEnumerable與索引不同。 我應該使用foreach並創建一個新數組嗎? 還是可以在不創建新數組的情況下做到這一點?
int[,] myArray = new int[2, 2];
myArray[0,0] = 1;
myArray[1,1] = 2;
myArray[0,0] = 3;
myArray[1,1] = 4;
foreach (var value in myArray)
{
Console.Write(value);
}
var valueAtIndex = myArray[2]; //Won't compile, error: Wrong number of indices inside []; expected 2
如果用一維索引表示一個索引,該索引按從上到下從左到右的順序環繞多維數組的行,則可以使用以下公式從中計算x
和y
:
x = index % width
y = index / width
如果您不僅要讀取值,還希望以與foreach
遍歷數組相同的順序設置它們,則可以使用以下常規索引器類:
public class ArrayIndexer
{
readonly int totalLength;
readonly int lastIndexLength;
readonly int[] lengths;
readonly int[] lowerBounds;
int current;
readonly int[] currentZeroBased;
public ArrayIndexer(int[] lengths, int[] lowerBounds)
{
lastIndexLength = lengths[lengths.Length - 1];
totalLength = lengths[0];
for (int i = 1; i < lengths.Length; i++)
{
totalLength *= lengths[i];
}
this.lengths = lengths;
this.lowerBounds = lowerBounds;
currentZeroBased = new int[lengths.Length];
current = -1;
}
public bool MoveNext()
{
current++;
if (current != 0)
{
int currLastIndex = current % lastIndexLength;
currentZeroBased[currentZeroBased.Length - 1] = currLastIndex;
if (currLastIndex == 0)
{
for (int i = currentZeroBased.Length - 2; i >= 0; i--)
{
currentZeroBased[i]++;
if (currentZeroBased[i] != lengths[i])
break;
currentZeroBased[i] = 0;
}
}
}
return current < totalLength;
}
public int[] Current
{
get
{
int[] result = new int[currentZeroBased.Length];
for (int i = 0; i < result.Length; i++)
{
result[i] = currentZeroBased[i] + lowerBounds[i];
}
return result;
}
}
}
您可以像這樣使用它來設置整個數組:
int[,] myArray = new int[2, 2];
ArrayIndexer arrayIndexer = new ArrayIndexer(new[] {2, 2}, new[] {0, 0});
int i = 0;
while (arrayIndexer.MoveNext())
{
myArray.SetValue(++i, arrayIndexer.Current);
}
從另一端提出要求怎么樣?
int rows=2, cols=2;
int[] myArray = new int[rows*cols];
public void setMyArray(int[] myArray, int x, int y, int value)
{
myArray[(y)*cols + x] = value;
}
setMyArray(0,0,1);
setMyArray(0,1,2);
setMyArray(1,0,3);
setMyArray(1,1,4);
對於枚舉器,它非常簡單-僅允許順序訪問。 在內部,它保存一個索引數組,並將它們一個接一個地遞增。 它不使用任何技巧使數組成為一維或其他任何東西-這將嚴重違反框架保證。 而且也沒有收益,這是在實現收益之前就寫的:)
當然,您可以使用unsafe
代碼自己編寫此類hack。 但是,這是一個非常糟糕的主意。 你真的不知道怎么陣列等存儲器被組織-這是運行時的實現細節。 只是不要。
而是,找出一個合理的尋址方案。 如果只有一個索引有意義,那么為什么數組首先是多維的呢? 如果使數組具有多維性是有意義的,為什么要以單維方式訪問它呢?
如果確實有道理,出於某種原因,您可能想圍繞某個內部數組創建自己的包裝器類,而不是僅使用數組。 就這么簡單
public class DualAccessArray<T>
{
private readonly T[,] _internalArray;
private readonly int _width;
private readonly int _height;
public DualAccessArray(int width, int height)
{
_width = width;
_height = height;
_internalArray = new T[width, height];
}
public T this[long index]
{
get { return _internalArray[index / _width, index % _width]; }
set { _internalArray[index / _width, index % _width] = value; }
}
public T this[int x, int y]
{
get { return _internalArray[x, y]; }
set { _internalArray[x, y] = value; }
}
}
然后您可以使用像
var ar = new DualAccessArray<string>(10, 10);
ar[15] = "Hi!";
Console.WriteLine(ar[1, 5]);
您可以滿足您的需求。 例如,使用不同類型的尋址和內存布局可能更有意義,將數組設為一維可能更有意義,您可能希望包裝一個現有數組……這一切都取決於您要使用的內容實際嘗試做。
Foreach可以做到
不,它不能。 真的沒有比喻。
由於多維數組實際上是一個具有多個維的數組,因此您有三個選擇。
停止使用多維數組。 如果這種操作占主導地位,則數組數組(“鋸齒狀數組”)可能會更符合您的需求。
忽略數組的詳細信息,並使用指向內存的指針。 如果不是非常必要,最好避免。
只需使用兩個索引。 您可以很容易地找到其他索引的范圍,因此可以做到。
下面的操作將在二維數組上進行,並且在給定第一個索引的值時,將迭代該索引的所有值。
public static IEnumerable<T> GetRow<T>(this T[,] array, int index)
{
int last = array.GetUpperBound(1);
for(int idx = array.GetLowerBound(1); idx <= last; ++idx)
yield return array[index, idx];
}
在這里回答標題。 假設我們需要使用一維索引訪問任意維數組。
var test = new [,,,]
{
{
{
{ 00, 01, 02 }, { 03, 04, 05 }, { 06, 07, 08 }
},
{
{ 09, 10, 11 }, { 12, 13, 14 }, { 15, 16, 17 }
},
{
{ 18, 19, 20 }, { 21, 22, 23 }, { 24, 25, 26 }
}
},
{
{
{ 27, 28, 29 }, { 30, 31, 32 }, { 33, 34, 35 }
},
{
{ 36, 37, 38 }, { 39, 40, 41 }, { 42, 43, 44 }
},
{
{ 45, 46, 47 }, { 48, 49, 50 }, { 51, 52, 53 }
}
}
};
Func<int, IEnumerable<int>, IEnumerable<int>> unwrapLinearIndex = (linidx, bounds) =>
bounds.Select((b, i) => bounds.Take(i).Aggregate(linidx, (acc, bnd) => acc / bnd) % b);
// Reverse() to enumerate innermost dimensions first
var testBounds = new int[test.Rank].Select((_, d) => test.GetUpperBound(d) + 1).Reverse().ToArray();
for (int i = 00; i < test.Length; i++)
{
var indexes = unwrapLinearIndex(i, testBounds).Reverse().ToArray();
Console.Write($"{test.GetValue(indexes)} ");
}
輸出為0 1 2 3 4... 53
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.