共通の初期値を大きな配列に効率的に割り当てるにはどうすればよいですか? たとえば、すべての初期値がゼロである 100 x 100 x 100 の整数配列があるとします。
matlab では、次のように書くだけです。
array = zeros(100,100,100);
C#でループなしでこれを行うにはどうすればよいですか?
共通の初期値を大きな配列に効率的に割り当てるにはどうすればよいですか? たとえば、すべての初期値がゼロである 100 x 100 x 100 の整数配列があるとします。
matlab では、次のように書くだけです。
array = zeros(100,100,100);
C#でループなしでこれを行うにはどうすればよいですか?
議論のために(個人的には良いプログラミングスタイルだとは思いません)、C#で1行で、技術的にはループなしでLINQで実行できます
int[,,] cube = new int[A, B, C];
Enumerable.Range(0, A*B*C).Select(i => cube[i/(B*C), i%(B*C)/C, i%C] = 1).Count();
ここでの Count() は、シーケンスを列挙するためにのみ必要であり、その結果は無視されます。これは、MATLAB の ones() 関数を実装するためのものです。zeros() を実装するには、以下を使用できます。
Array.Clear(cube, 0, A*B*C);
public void SetAllValues(int[,,] data, int value) {
for (int x = 0; x < data.GetLength(0); x++) {
for (int y = 0; y < data.GetLength(1); y++) {
for (int z = 0; z < data.GetLength(2); z++) {
data[x, y, z] = value;
}
}
}
}
Enumerable.Repeat
その目的のために次のように使用できます。
int[] zeros1Dim = Enumerable.Repeat(0, 100).ToArray();
int[][] zeros2Dim = Enumerable.Repeat(zeros1Dim, 100).ToArray();
int[][][] array = Enumerable.Repeat(zeros2Dim, 100).ToArray();
C#で配列を初期化する慣用的な非ループの方法はわかりません。ここでの回答に基づくと、他の方法もそうではないようです。安全でないメモリコピーは、実行していることの意味を実際に混乱させますが、必要なコード行が少なくなる可能性があります。セマンティクスよりもメカニズム(この値をこのメモリアドレスに移動)を強調します(この多次元配列全体を同じ値。)
それを考えると、私の提案は、弾丸を噛み、ループを使用することですが、それから、心地よい名前のメソッドでそれを抽象化します:
public static T[,,] FillArrayWithValue<T>(this T[,,] array, T value)
{
// Loops go here
}
それを静的ユーティリティクラスのどこかに貼り付けると、コールサイトは次のようになります。
int[,,] myArray = new int[4, 5, 6];
myArray.FillArrayWithValue(value);
C プログラミングの世界では、 memcpyやCopyMemoryなどの OS 関数を使用して、メモリをある場所から別の場所に効率的に移動するのが一般的です。これは、オペレーティング システムが専用ハードウェア ( DMA ) を使用して操作を実行するため、ループを使用するよりもはるかに優れています。
これがあなたの単純なケースに役立つかどうかはわかりませんが、必要に応じて特定の DLL からこれらの関数をインポートすることができます。
[DllImport("kernel32.dll")]
static extern void CopyMemory(IntPtr destination, IntPtr source, uint length);
static void Main(string[] args)
{
byte[] a = new byte[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
byte[] b = new byte[10];
IntPtr pa = Marshal.UnsafeAddrOfPinnedArrayElement(a, 0);
IntPtr pb = Marshal.UnsafeAddrOfPinnedArrayElement(b, 0);
CopyMemory(pb, pa, 10);
}
CopyMemory関数は連続したメモリ ブロックに対してのみ機能するため、呼び出し方に注意してください。
重要な編集:
以下の解説を見ると、上記のコードが「使用可能」とはほど遠いことに気付くでしょう。より良いアプローチは、codesparkle で提案されているように、 Buffer.BlockCopy メソッドを使用することです。これはおそらく内部的に同じことを安全な方法で行います。
byte[] a = new byte[10]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
byte[] b = new byte[10];
Buffer.BlockCopy(a, 0, b, 0, 10);