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


101

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

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

これを行うより良い方法はありますか?

更新:多分C#に次のような組み込みメカニズムがあるといいのですが。

foreachbackwards (int i in myArray)
{
    // so easy
}

アップデート2:存在しているより良い方法は。ルーンは賞品を受け取ります:

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
}

良さが明快さや保守性を含んでいる場合、代替案のどれがより良いのか、私にはわかりません。
dkretz 2008年

そうだね。私はかなり頻繁に行う必要があるため、これを行うためのより合理的な方法を見つけることを望んでいました。
MusiGenesis 2008年

3
この質問のほとんどは、以下の回答のレビューのようなものです。大幅に短縮することをお勧めします。
einpoklum 2013年

タイトルとハッシュタグからCとC ++を削除してください。
jaskmar

回答:


148

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

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

32
私が最初にあなたの答えを読んだとき、それはコンパイルすらできないように見えたので、私はあなたがクレイジーな人だと思いました。しかし、それはまさに私が探していたものです。逆方向のforループを書くためのより良い方法です。
MusiGenesis 2008年

3
i-> 0; わざとです。それは、彼が「活字どうぞ」によって何を意味するかだ
ヨハネス・シャウブ- litb

16
私はそれが「誤植的に混乱している」と私は思いました。私にとって、「-」は、影響を与える変数に隣接していない限り、正しく見えません。
MusiGenesis 2008年

26
それはあまりにもあいまいで難読化されています。私はこのようなものをプロダクションコードで決して記述しません...
Mihai Todor

9
Aah the go to operator(->)がトリックを実行します!
nawfal 2014年

118

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はありませ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; .... */
}

インデックスの使用

std::size_t上記とは対照的に、ここでは安全に使用できます。これsizeofは、常に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我々は唯一のコンパイル時の評価とゼロ実行時のオーバーヘッドで、探しているものであるN * 1、:のを。

する代わりに

(sizeof a / sizeof *a)

コードを変更して、今すぐできるようにします。

(sizeof array_size(a))

あなたのコンテナが逆イテレータを持っていない場合、また、あなたはブーストのreverse_iterator実装を使用することができます。boost.org/doc/libs/1_36_0/libs/iterator/doc/...
MP24

あなたのarray_sizeは静的に割り当てられた配列では機能するようですが、たとえばaが 'new int [7]'だった場合は失敗しました。
ネイトパーソンズ

2
ええ、それがその意図です:) new int [7]はポインタを返します。したがって、sizeof(new int [7])は7 * sizeof(int)ではなく、sizeof(int *)を返します。array_sizeは、サイレントに動作するのではなく、その場合にコンパイルエラーを引き起こします。
Johannes Schaub-litb 2008

また、array_size(+ statically_allocated_array)も試してください。これは、+演算子が配列を最初の要素へのポインタにするために失敗します。プレーンなsizeofを使用すると、ポインタのサイズが再び表示されます
Johannes Schaub-litb

まず第一に-なぜ単に逆の順序で使用endしないのbeginですか?
トマーシュZato -復活モニカ

54

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

(少なくとも私にとっては)間違いを犯しやすいので、このスニペットを挿入することをお勧めします。

それは私が好き、言ったArray.Reverse()/ Enumerable.Reverse()して、反復は、前方より良い-彼らはより明確に意図述べます。


41

私は常に、「活版印刷的に楽しい」コードよりも明確なコードを好みます。したがって、私は常に使用します:

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

逆方向にループする標準的な方法と考えることができます。
ちょうど私の2セント...


働いた。C#ではまだこれを行っていませんが、ありがとうございます。
PCPGMR 2014年

では、配列が本当に大きい場合はどうでしょう(インデックスは符号なしの型でなければなりません)。size_tは実際には署名されていませんか?
lalala

iをuintとして宣言できます。MarcGravellの投稿を参照してください。
ジャックグリフィン

文字体裁的に楽しいものとは異なり、ラップのために符号なしの型では機能しません。良くない。
オセロット

17

ではC#の使用してLINQのを

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

これは確かに最も単純ですが、CLRの現在のバージョンでは、リストの反転は常にO(1)操作ではなくO(n)操作であり、IList <T>の場合はそうであることに注意してください。connect.microsoft.com/VisualStudio/feedback/…を
Greg Beech

1
.Reverse()は配列のローカルコピーを作成し、それを逆方向に反復処理するように見えます。配列を反復処理できるように配列をコピーするのは、一種の非効率的なようです。"Linq"が含まれている投稿は、賛成票に値すると思います。:)
MusiGenesis 2008年

トレードオフの可能性がありますが、「回答に投票」(i-> 0)の結果、コードが遅くなる可能性があるという問題が発生します。これは、(i)が配列のサイズに対して毎回チェックされる必要があるためです。インデックスとして使用されます。
Keltex 2008年

1
Linqは素晴らしいと私は同意しますが、このアプローチの問題は、イテレーターが配列の項目を選択的に選択することを許可しないことです-全体ではなく、配列の一部をナビゲートしたい状況を考慮してください事...
jesses.co.tt 2016

11

これは、長さが符号付き整数型である配列にとって間違いなく最良の方法です。長さが符号なし整数型(たとえば、std::vectorC ++の場合)の配列の場合、終了条件を少し変更する必要があります。

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

たった今言った場合i >= 0、これは符号なし整数に対して常に当てはまるため、ループは無限ループになります。


C ++では、iが0の場合、「i--」は自動的に最大値に折り返しますか?すみません、私はC ++に障害があります。
MusiGenesis 2008年

すごい。皆さん、私が12年前に書いたもののハードドライブがいっぱいになるバグの原因を理解するのを手伝ってくれました。C ++で動作しないのは良いことです。
MusiGenesis 2008年

終了条件は次のとおりです:i <myArray.size()。unsigned intのオーバーフロープロパティにのみ依存します。整数とキャストの実装の詳細ではありません。その結果、代替の整数表現を持つ言語で読みやすく、機能します。
ejgottl 2008年

私にとってより良い解決策は、誰も傷つけないC#の世界にとどまることです。
MusiGenesis 2008年

4

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

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

(実際には、ここでもarr.Length = uint.MaxValueなどのケースに注意する必要があります...多分!=どこか...もちろん、それは非常にまれなケースです!)


ただし、「-pos」の扱いには注意が必要です。私は過去に、コードでランダムに「修正」することを好み、正しくないように思われる同僚がいました。
MusiGenesis 2008年

1
次に、.arr.Length-1とpos--を使用します。–
Marc Gravell

4

CIではこれをしたい:


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

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

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

私はこれが好き。あなたの方法が機能することを理解するのに1、2秒かかりました。追加されたC#バージョンを気にしないでください(追加の括弧は、インデックスがforループと同じスコープになるようにするためです)。
MusiGenesis 2008年

これをプロダクションコードで使用するかどうかはわかりません。普遍的な「WTFはこれですか?」維持する必要がある人からの反応ですが、実際には通常のforループよりも入力が簡単で、マイナス記号や> =記号はありません。
MusiGenesis 2008年

1
C#バージョンには余分な "> 0"が必要です。
MusiGenesis 2008年

どんな言語なのかよくわかりません。しかし、最初のコードスニペットは確かにC :)ではありません。多分あなたはC#を書きたかったのですが、htmlはそれを解析できませんでしたか?
Johannes Schaub-litb 2008年

@litb: "myArray.Length"は有効なC(?)ではないと思いますが、whileループ部分は機能し、見栄えが良いです。
MusiGenesis 2008年

3

C ++でこれを行う最良の方法は、反復子(またはより優れた範囲)アダプターを使用することです。

基本的に、

vector<value_type> range;
foreach(value_type v, range | reversed)
    cout << v;

範囲「範囲」(ここでは空ですが、要素を自分で追加できると確信しています)を逆の順序で表示します。もちろん、単に範囲を反復することはあまり役に立ちませんが、その新しい範囲をアルゴリズムやその他のものに渡すことはかなりクールです。

このメカニズムは、さらに強力な用途にも使用できます。

range | transformed(f) | filtered(p) | reversed

関数「f」がすべての要素に適用される範囲「範囲」を遅延計算します。「p」が真でない要素は削除され、最終的に結果の範囲が逆になります。

パイプ構文は、インフィックスを考えると、最も読みやすいIMOです。Boost.Rangeライブラリの更新保留中のレビューはこれを実装しますが、自分で行うのも非常に簡単です。関数fと述語pをインラインで生成するラムダDSELを使用すると、さらにクールになります。


"foreach"の場所を教えていただけますか?BOOST_FOREACHの#defineですか?ずっとかわいいですね。
Johannes Schaub-litb


1

私はwhileループを好みます。iforループの条件でデクリメントするよりも私にはより明確です

int i = arrayLength;
while(i)
{
    i--;
    //do something with array[i]
}

0

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

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

-1

ここで自分の質問に答えてみますが、これもあまり好きではありません。

for (int i = 0; i < myArray.Length; i++)
{
    int iBackwards = myArray.Length - 1 - i; // ugh
    myArray[iBackwards] = 666;
}

.Length-1-iを毎回行うのではなく、おそらく2番目の変数を検討しますか?私の[更新された]投稿を見てください。
Marc Gravell

私自身の質問に反対票を投じました。厳しい。オリジナルよりも不格好ではありません。
MusiGenesis 2008年

-4

注:この投稿は非常に詳細になり、トピックから外れたため、お詫び申し上げます。

それは私の同僚がそれを読んで、それが「どこか」で貴重であると信じていると言われています。このスレッドは場所ではありません。これがどこに行くべきかについてのフィードバックをいただければ幸いです(私はこのサイトを初めて利用しました)。


とにかく、これは.NET 3.5のC#バージョンであり、定義されたセマンティクスを使用して任意のコレクション型で機能するという点で驚くべきものです。これはデフォルトの測定(再利用)であり、ほとんどの一般的な開発シナリオでのパフォーマンスやCPUサイクルの最小化ではありませんが、現実の世界では発生するようには見えません(時期尚早の最適化)。

***任意のコレクション型に対して機能し、型の単一の値を期待してアクションデリゲートをとる拡張メソッド。すべての項目に対して逆に実行されます**

要件3.5:

public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
      {
          foreach (var contextItem in sequenceToReverse.Reverse())
              doForEachReversed(contextItem);
      }

古い.NETバージョン、またはLinqの内部をよりよく理解したいですか?読んでください。

ASSUMPTION:.NET型システムでは、Array型はIEnumerableインターフェイスから継承します(汎用のIEnumerableではなく、IEnumerableのみ)。

最初から最後まで繰り返す必要があるのはこれだけですが、反対方向に移動したいとします。IEnumerableはタイプ「オブジェクト」の配列で機能するため、どのタイプでも有効です。

重要な測定:シーケンスを「より良い」逆の順序で処理できる場合は、整数に対してのみ実行できると想定しています。

.NET CLR 2.0-3.0のソリューションa:

説明:含まれている各インスタンスが同じタイプであることを要求するIEnumerable実装インスタンスをすべて受け入れます。したがって、配列を受け取った場合、配列全体にはタイプXのインスタンスが含まれます。他のインスタンスがタイプ!= Xの場合は、例外がスローされます。

シングルトンサービス:

パブリッククラスReverserService {プライベートReverserService(){}

    /// <summary>
    /// Most importantly uses yield command for efficiency
    /// </summary>
    /// <param name="enumerableInstance"></param>
    /// <returns></returns>
    public static IEnumerable ToReveresed(IEnumerable enumerableInstance)
    {
        if (enumerableInstance == null)
        {
            throw new ArgumentNullException("enumerableInstance");
        }

        // First we need to move forwarad and create a temp
        // copy of a type that allows us to move backwards
        // We can use ArrayList for this as the concrete
        // type

        IList reversedEnumerable = new ArrayList();
        IEnumerator tempEnumerator = enumerableInstance.GetEnumerator();

        while (tempEnumerator.MoveNext())
        {
            reversedEnumerable.Add(tempEnumerator.Current);
        }

        // Now we do the standard reverse over this using yield to return
        // the result
        // NOTE: This is an immutable result by design. That is 
        // a design goal for this simple question as well as most other set related 
        // requirements, which is why Linq results are immutable for example
        // In fact this is foundational code to understand Linq

        for (var i = reversedEnumerable.Count - 1; i >= 0; i--)
        {
            yield return reversedEnumerable[i];
        }
    }
}



public static class ExtensionMethods
{

      public static IEnumerable ToReveresed(this IEnumerable enumerableInstance)
      {
          return ReverserService.ToReveresed(enumerableInstance);
      }
 }

[TestFixture] public class Testing123 {

    /// <summary>
    /// .NET 1.1 CLR
    /// </summary>
    [Test]
    public void Tester_fornet_1_dot_1()
    {
        const int initialSize = 1000;

        // Create the baseline data
        int[] myArray = new int[initialSize];

        for (var i = 0; i < initialSize; i++)
        {
            myArray[i] = i + 1;
        }

        IEnumerable _revered = ReverserService.ToReveresed(myArray);

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
    }

    [Test]
    public void tester_why_this_is_good()
    {

        ArrayList names = new ArrayList();
        names.Add("Jim");
        names.Add("Bob");
        names.Add("Eric");
        names.Add("Sam");

        IEnumerable _revered = ReverserService.ToReveresed(names);

        Assert.IsTrue(TestAndGetResult(_revered).Equals("Sam"));


    }

    [Test]
    public void tester_extension_method()
  {

        // Extension Methods No Linq (Linq does this for you as I will show)
        var enumerableOfInt = Enumerable.Range(1, 1000);

        // Use Extension Method - which simply wraps older clr code
        IEnumerable _revered = enumerableOfInt.ToReveresed();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }


    [Test]
    public void tester_linq_3_dot_5_clr()
    {

        // Extension Methods No Linq (Linq does this for you as I will show)
        IEnumerable enumerableOfInt = Enumerable.Range(1, 1000);

        // Reverse is Linq (which is are extension methods off IEnumerable<T>
        // Note you must case IEnumerable (non generic) using OfType or Cast
        IEnumerable _revered = enumerableOfInt.Cast<int>().Reverse();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }



    [Test]
    public void tester_final_and_recommended_colution()
    {

        var enumerableOfInt = Enumerable.Range(1, 1000);
        enumerableOfInt.PerformOverReversed(i => Debug.WriteLine(i));

    }



    private static object TestAndGetResult(IEnumerable enumerableIn)
    {
      //  IEnumerable x = ReverserService.ToReveresed(names);

        Assert.IsTrue(enumerableIn != null);
        IEnumerator _test = enumerableIn.GetEnumerator();

        // Move to first
        Assert.IsTrue(_test.MoveNext());
        return _test.Current;
    }
}

2
おいまたはdudette:私は "for(int i = myArray.Length-1; i> = 0; i--)"と書くのにあまり扱いにくい方法を探していました。
MusiGenesis 2008年

1
この返信にはまったく
意味
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.