web-dev-qa-db-ja.com

C / C#/ C ++で後方ループを実行する最良の方法は何ですか?

配列を逆方向に移動する必要があるため、次のようなコードがあります。

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
}
91
MusiGenesis

確かに少しあいまいですが、これを行うための最も活版印刷で楽しい方法は

for (int i = myArray.Length; i --> 0; )
{
    //do something
}
132
Rune

C++では、基本的に、イテレータを使用して反復するか、インデックスを使用するかを選択できます。プレーン配列かstd::vectorかによって、異なる手法を使用します。

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]; ... */
}

Sizeofをポインターに適用して落とし穴を回避する

実際、上記の方法で配列のサイズを決定するのは面倒です。 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))
111

C#の場合、Visual Studio 2005以降を使用する場合「forr」と入力して[TAB] [TAB]を押します。これは、コレクションを逆方向に進むforループに展開されます。

(少なくとも私にとっては)間違いを犯しやすいので、このスニペットを入れるのは良い考えだと思いました。

とはいえ、私はArray.Reverse()/Enumerable.Reverse()が好きで、それからforwardsをより良く繰り返します-彼らはより明確に意図を述べています。

52
Jay Bazuzi

私はalways 'typographically pleasing'コードに対して明確なコードを好みます。したがって、私は常に使用します:

for (int i = myArray.Length - 1; i >= 0; i--)  
{  
    // Do something ...  
}    

これを後方ループの標準的な方法と見なすことができます。
ちょうど2セントです...

36
Jack Griffin

In C# using Linq

foreach(var item in myArray.Reverse())
{
    // do something
}
17
Keltex

これは、符号付き整数型の長さを持つ配列にとって間違いなく最善の方法です。長さが符号なし整数型である配列の場合(例:std::vector C++では)、終了条件をわずかに変更する必要があります。

for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
    // blah

i >= 0、これは符号なし整数に対して常に真であるため、ループは無限ループになります。

10
Adam Rosenfield

は、私にはよく見えますよ。インデクサーが署名されていない場合(uintなど)、それを考慮する必要があります。私を怠zyと呼びますが、その場合(符号なし)の場合、カウンター変数を使用するだけです。

uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
    arr[--pos] = 42;
}

(実際には、ここでもarr.Length = uint.MaxValue ...などのケースに注意する必要があります。

4
Marc Gravell

Cでは、これを行うのが好きです:


int i = myArray.Length;
while (i--) {
  myArray[i] = 42;
}

MusiGenesisによって追加されたC#の例:

{int i = myArray.Length; while (i-- > 0)
{
    myArray[i] = 42;
}}
4
Twotymz

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をインラインで生成するのがさらに便利です。

3
loufoque

Whileループの方が好きです。 forループの条件でiをデクリメントするよりも明確です

int i = arrayLength;
while(i)
{
    i--;
    //do something with array[i]
}
1
Petko Petkov
// this is how I always do it
for (i = n; --i >= 0;){
   ...
}
1
Mike Dunlavey

元の質問のコードを使用しますが、foreachを使用して、C#で整数インデックスを使用したい場合:

foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
    myArray[i] = 42; 
}
0
xyz

良い点に明確性や保守性が含まれている場合、代替案のいずれが優れているのかがわかりません。

0
dkretz