foreachを通じて逆方向に反復することは可能ですか?


129

forステートメントを使用しても同じ効果が得られることはわかっていforeachますが、C#のループを逆方向にループできますか?


6
リスト内の要素(list.Reverse())を逆にすることができます。
Akanthan 2013


順序を逆にできるように、列挙内のすべての要素をインスタンス化する必要があります。できない場合があります。シーケンスを検討してください:IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }どのようにそれを逆転しますか?
Suncat2000

回答:


84

リストを使用する場合(直接インデックス付け)、forループを使用する場合ほど効率的に行うことはできません。

編集:これは通常、ループを使用できる場合に、forこのタスクに適した方法である可能性が高いことを意味します。さらに、foreach順序どおりに実装されている限り、構造自体は、要素のインデックスや反復順序に依存しないループを表現するために構築されます。これは、並列プログラミングで特に重要です。私の意見反復は使用してはならないために頼ることをforeachループするために。


3
最後の発言は少し一般的すぎると思います。たとえば、ある種のIEnumerableを順番に反復する必要がある場合は確かにありますか?その場合は、foreachを使用しませんか?何を使いますか?
avl_sweden 2017年

1
更新:Linqを使用し、両方のリストと他の列挙可能なものについて議論するより現代的な回答については、[類似の質問に対するブライアンの回答[(stackoverflow.com/a/3320924/199364)]を参照してください。
ToolmakerSteve 2017年

更新:Jon Skeetはさらにエレガントな答えを持っています。これは " yield return" を使用して可能になりました。
ToolmakerSteve 2017

2
私は@avl_swedenと同じ最初の反応がありました-「順序に依存する反復はforeachを使用すべきではありません」という音は広範に渡ります。はい、foreachは順序に依存しない並列タスクを表現するのに適しています。しかし、それはまた、現代のイテレータベースのプログラミングの不可欠な部分です。おそらくポイントは、反復が順序に依存しないと主張するかどうかを明確にするために、2つの異なるキーワードがあった方がより明確/安全になるということでしょうか。[コントラクトを伝播できるEiffelのような言語を考えると、そのようなアサーションは、指定されたコードに対して真または偽である可能性があります。]
ToolmakerSteve

2
foreachが「要素のインデックスと反復順序に依存しないループを表現するために構築されている」という考えは正しくありません。C#言語仕様では、foreachプロセス要素を順番に使用する必要があります。イテレーターでMoveNextを使用するか、配列の反復ごとにゼロから開始して1ずつ増加するインデックスを処理します。
ドナルドリッチ

145

.NET 3.5を使用している場合、これを行うことができます。

IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())

基本的に列挙子を順方向に進めてすべてをスタックに配置し、すべてを逆の順序でポップアウトする必要があるため、あまり効率的ではありません。

直接インデックス可能なコレクション(IListなど)がある場合は、必ずforループを使用する必要があります。

.NET 2.0を使用していてforループを使用できない場合(つまり、IEnumerableしかない場合)は、独自のReverse関数を作成する必要があります。これはうまくいくはずです:

static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
    return new Stack<T>(input);
}

これはおそらく明らかではないいくつかの動作に依存しています。IEnumerableをスタックコンストラクターに渡すと、それが反復処理され、アイテムがスタックにプッシュされます。その後、スタックを反復処理すると、逆の順序でポップされます。

これと.NET 3.5 Reverse()拡張メソッドは、アイテムの返却を決して停止しないIEnumerableをフィードすると明らかに爆発します。


また、私は.net v2のみを言及するのを忘れていました
JL。

4
興味深い.NET 2.0ソリューション。
RichardOD 2009

11
何か不足していますか、それとも.Net 3.5ソリューションは実際には機能しませんか?Reverse()は、リストを元の場所に戻し、それを返しません。私はそのような解決策を望んでいたので、残念です。
user12861 2012年

2
一部の管理者は、この回答に誤ったマークを付けています。Reverse()はvoid [1]であるため、上記の例ではコンパイルエラーが発生します。[1] msdn.microsoft.com/en-us/library/b0axc2h2(v=vs.110).aspx
MickyD 2014年

6
@ user12861、Micky Duncan:答えは間違っていません。何かが欠けています。System.Collections.Generic.List <T>には、インプレースリバースを行うReverseメソッドがあります。.Net 3.5では、Reverseという名前のIEnumerable <T>に拡張メソッドがあります。これをより明確にするために、例をvarからIEnumerable <int>に変更しました。
マットハウエルズ2014

55

280Z28が言うように、IList<T>あなたはただインデックスを使うことができます。これを拡張メソッドで隠すことができます:

public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
    for (int i = items.Count-1; i >= 0; i--)
    {
        yield return items[i];
    }
}

これはEnumerable.Reverse()、最初にすべてのデータをバッファリングするよりも高速になります。(このReverseように最適化が適用されているとCount()は思いません。)このバッファリングは、最初に反復を開始したときにデータが完全に読み取られることを意味しFastReverseますが、反復中にリストに加えられた変更を「確認」します。(繰り返しの間に複数のアイテムを削除した場合も壊れます。)

一般的なシーケンスの場合、逆に反復する方法はありません。たとえば、シーケンスは無限である可能性があります。

public static IEnumerable<T> GetStringsOfIncreasingSize()
{
    string ret = "";
    while (true)
    {
        yield return ret;
        ret = ret + "x";
    }
}

それを逆に反復しようとするとどうなると思いますか?


好奇心だけで、なぜ "> -1"ではなく "> = 0"を使用するのですか
クリスS

15
>なぜ "> -1"ではなく "> = 0"を使用するのですか?> = 0は、コードを読む人間に意図をよりよく伝えるためです。コンパイラーは、パフォーマンスを改善する場合、それを> -1に最適化できる必要があります。
マークマスラー2009

1
FastReverse(this IList <T> items)はFastReverse <T>(this IList <T> items。:)である必要があります
Rob

スティックの分割0 <= iはさらに良いです。何年にもわたってかなりの数の子供たちの数学を教え、コーディングで人々を助けた後、物事が行の自然な順序で来るので、人々が常にa> bからb <aに交換する場合に発生するエラーを大幅に減らすことがわかりましたの数(または座標系のX軸)-唯一の欠点は、方程式をXMLに貼り付ける必要がある場合です。たとえば、.config
Eske Rahn

13

foreach反復に使用する前に、次のreverse方法でリストを逆にします。

    myList.Reverse();
    foreach( List listItem in myList)
    {
       Console.WriteLine(listItem);
    }


myListが役立つかについての注意の言葉。IEnumerable.Reverseはここでは機能しません。
nawfal

2
@ MA-Maddin 2つの違いはありません。回答者はList <T> .Reverseに依存していると思います。
nawfal

実はなぜそう言ったのかわかりません。以来、私はとにかく、これはコードを混乱さ...詳細を追加していなければならないmyListタイプであると思われるSystem.Collections.Generic.List<System.Windows.Documents.List>(またはその他のカスタムListP:タイプ)そうでない場合は、このコードは動作しません
マーティン・シュナイダー

5

インデックスを作成する余裕がない場合や、Linqクエリの結果を元に戻したい場合、またはソースコレクションを変更したくない場合があります。これらのいずれかに該当する場合、Linqが役立ちます。

Linq Selectで匿名型を使用するLinq拡張メソッド。LinqOrderByDescendingにソートキーを提供します。

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

使用法:

    var eable = new[]{ "a", "b", "c" };

    foreach(var o in eable.Invert())
    {
        Console.WriteLine(o);
    }

    // "c", "b", "a"

「リバース」と同義であり、リストリバース実装とのあいまいさをなくすことができるため、「インバート」という名前が付けられています。

Int32.MinValueとInt32.MaxValueはあらゆる種類のコレクションインデックスの範囲外であるため、コレクションの特定の範囲を逆にすることもできます。これらを使用して、順序付けプロセスを実行できます。要素のインデックスが指定された範囲を下回っている場合、OrderByDescendingを使用しても順序が変更されないように、Int32.MaxValueが割り当てられます。同様に、指定された範囲より大きいインデックスにある要素には、Int32.MinValueが割り当てられます。注文プロセスの最後に表示されます。指定された範囲内のすべての要素には通常のインデックスが割り当てられ、それに応じて逆になります。

    public static IEnumerable<T> Invert<T>(this IEnumerable<T> source, int index, int count)
    {
        var transform = source.Select(
            (o, i) => new
            {
                Index = i < index ? Int32.MaxValue : i >= index + count ? Int32.MinValue : i,
                Object = o
            });

        return transform.OrderByDescending(o => o.Index)
                        .Select(o => o.Object);
    }

使用法:

    var eable = new[]{ "a", "b", "c", "d" };

    foreach(var o in eable.Invert(1, 2))
    {
        Console.WriteLine(o);
    }

    // "a", "c", "b", "d"

これらのLinq実装のパフォーマンスへの影響と、一時的なListを使用してコレクションを逆転するためにラップすることの違いについては、よくわかりません。


執筆の時点では、Linqの独自のリバース実装を認識していませんでしたが、これを実行するのは楽しかったです。 https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx


4

List <T>を使用する場合は、次のコードも使用できます。

List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();

これは、それ自体でリストを逆に書き込むメソッドです。

今、foreach:

foreach(string s in list)
{
    Console.WriteLine(s);
}

出力は次のとおりです。

3
2
1

3

IEnumerableまたはIEnumerableを実装するコレクションコード(たとえば、IListの独自の実装)を変更できる場合可能です

たとえば、次の実装を介して、この作業を実行するイテレータを作成します。 IEnumerableインターフェイスます( 'items'はこのサンプルのリストフィールドであると想定しています)。

public IEnumerator<TObject> GetEnumerator()
{
    for (var i = items.Count - 1; i >= 0; i--)
    { 
        yield return items[i];
    }
}

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

このため、リストはリストを逆順に処理します。

ヒント:ドキュメント内のリストのこの特別な動作を明確に記載する必要があります(StackやQueueなどの自己説明的なクラス名を選択することもできます)。


2

いいえ。ForEachは各項目のコレクションを反復処理するだけで、順序はIEnumerableを使用するかGetEnumerator()を使用するかによって異なります。


1
コレクションのタイプによっては、順序の保証があります
ジョンスキート、

1

Jon Skeetによる素敵な答えについての詳細、これは多目的に使用できます。

public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
    if (Forwards) foreach (T item in items) yield return item;
    else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}

そして次に使用する

foreach (var item in myList.Directional(forwardsCondition)) {
    .
    .
}

-1

私は働いたこのコードを使用しました

                if (element.HasAttributes) {

                    foreach(var attr in element.Attributes().Reverse())
                    {

                        if (depth > 1)
                        {
                            elements_upper_hierarchy_text = "";
                            foreach (var ancest  in element.Ancestors().Reverse())
                            {
                                elements_upper_hierarchy_text += ancest.Name + "_";
                            }// foreach(var ancest  in element.Ancestors())

                        }//if (depth > 1)
                        xml_taglist_report += " " + depth  + " " + elements_upper_hierarchy_text+ element.Name + "_" + attr.Name +"(" + attr.Name +")" + "   =   " + attr.Value + "\r\n";
                    }// foreach(var attr in element.Attributes().Reverse())

                }// if (element.HasAttributes) {

2
.Reverse()は実際にリストを変更しているため、これは悪いことです。
Colin Basnett、2016

-8

これはかなりうまくいきます

List<string> list = new List<string>();

list.Add("Hello");
list.Add("Who");
list.Add("Are");
list.Add("You");

foreach (String s in list)
{
    Console.WriteLine(list[list.Count - list.IndexOf(s) - 1]);
}

9
これは信じられないほど効率が悪いように思えます。
Jean Azzopardi、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.