配列を逆方向に移動する必要があるため、次のようなコードがあります。
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
これを行うより良い方法はありますか?
更新:C#に次のような組み込みメカニズムが組み込まれていることを望んでいました。
foreachbackwards (int i in myArray)
{
// so easy
}
更新2:そこにareより良い方法があります。ルーンは次のもので賞品を受け取ります:
for (int i = myArray.Length; i-- > 0; )
{
//do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
// do something
}
これは通常のCではさらに良く見えます(Twotymzのおかげです):
for (int i = lengthOfArray; i--; )
{
//do something
}
確かに少しあいまいですが、これを行うための最も活版印刷で楽しい方法は
for (int i = myArray.Length; i --> 0; )
{
//do something
}
C++では、基本的に、イテレータを使用して反復するか、インデックスを使用するかを選択できます。プレーン配列かstd::vector
かによって、異なる手法を使用します。
C++では、std::reverse_iterator:
を使用してこれを行うことができます。
for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
/* std::cout << *it; ... */
}
std::vector<T>::size
によって返される符号なし整数型は、not always std::size_t
です。大きくても小さくてもかまいません。これは、ループが機能するために重要です。
for(std::vector<int>::size_type i = someVector.size() - 1;
i != (std::vector<int>::size_type) -1; i--) {
/* std::cout << someVector[i]; ... */
}
符号なし整数型の値は、ビット数のモジュロによって定義されるため、機能します。したがって、-N
を設定している場合、(2 ^ BIT_SIZE) -N
になります
std::reverse_iterator
を使用して反復処理を行っています。
for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a);
it != itb;
++it) {
/* std::cout << *it; .... */
}
sizeof
は常に定義によりstd::size_t
を返すため、上記とは対照的に、ここではstd::size_t
を安全に使用できます。
for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) {
/* std::cout << a[i]; ... */
}
実際、上記の方法で配列のサイズを決定するのは面倒です。 aが実際には配列ではなくポインターである場合(これは非常に頻繁に発生し、初心者は混乱するでしょう)、静かに失敗します。より良い方法は、ポインターを指定するとコンパイル時に失敗する次のコードを使用することです。
template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];
渡された配列のサイズを最初に取得してから、同じサイズのchar型の配列への参照を返すことを宣言することで機能します。 char
は次のsizeof
を持つように定義されます:1.したがって、返される配列はsizeof
of:N * 1になります。時間評価とゼロの実行時オーバーヘッド。
する代わりに
(sizeof a / sizeof *a)
コードが変更されるようになりました
(sizeof array_size(a))
C#の場合、Visual Studio 2005以降を使用する場合「forr」と入力して[TAB] [TAB]を押します。これは、コレクションを逆方向に進むfor
ループに展開されます。
(少なくとも私にとっては)間違いを犯しやすいので、このスニペットを入れるのは良い考えだと思いました。
とはいえ、私はArray.Reverse()
/Enumerable.Reverse()
が好きで、それからforwardsをより良く繰り返します-彼らはより明確に意図を述べています。
私はalways 'typographically pleasing'コードに対して明確なコードを好みます。したがって、私は常に使用します:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something ...
}
これを後方ループの標準的な方法と見なすことができます。
ちょうど2セントです...
In C# using Linq:
foreach(var item in myArray.Reverse())
{
// do something
}
これは、符号付き整数型の長さを持つ配列にとって間違いなく最善の方法です。長さが符号なし整数型である配列の場合(例:std::vector
C++では)、終了条件をわずかに変更する必要があります。
for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
// blah
i >= 0
、これは符号なし整数に対して常に真であるため、ループは無限ループになります。
は、私にはよく見えますよ。インデクサーが署名されていない場合(uintなど)、それを考慮する必要があります。私を怠zyと呼びますが、その場合(符号なし)の場合、カウンター変数を使用するだけです。
uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
arr[--pos] = 42;
}
(実際には、ここでもarr.Length = uint.MaxValue ...などのケースに注意する必要があります。
Cでは、これを行うのが好きです:
int i = myArray.Length;
while (i--) {
myArray[i] = 42;
}
MusiGenesisによって追加されたC#の例:
{int i = myArray.Length; while (i-- > 0)
{
myArray[i] = 42;
}}
C++でこれを行うための最善の方法は、おそらく反復子(またはより良い範囲)アダプターを使用することです。これにより、シーケンスの走査中に遅延変換が行われます。
基本的に、
vector<value_type> range;
foreach(value_type v, range | reversed)
cout << v;
範囲「範囲」(ここでは空ですが、要素を自分で追加できると確信しています)を逆順で表示します。もちろん、範囲を単純に繰り返すことはあまり使用されませんが、その新しい範囲をアルゴリズムやものに渡すことはかなりクールです。
このメカニズムは、はるかに強力な用途にも使用できます。
range | transformed(f) | filtered(p) | reversed
関数「f」がすべての要素に適用され、「p」が真ではない要素が削除され、最終的に結果の範囲が反転する範囲「範囲」を遅延計算します。
パイプ構文は、中置記号であるため、最も読みやすいIMOです。 Boost.Rangeライブラリの更新保留中のレビューはこれを実装していますが、自分で行うのは非常に簡単です。ラムダDSELを使用すると、関数fと述語pをインラインで生成するのがさらに便利です。
Whileループの方が好きです。 forループの条件でi
をデクリメントするよりも明確です
int i = arrayLength;
while(i)
{
i--;
//do something with array[i]
}
// this is how I always do it
for (i = n; --i >= 0;){
...
}
元の質問のコードを使用しますが、foreachを使用して、C#で整数インデックスを使用したい場合:
foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
myArray[i] = 42;
}
良い点に明確性や保守性が含まれている場合、代替案のいずれが優れているのかがわかりません。