for
ステートメントを使用しても同じ効果が得られることはわかっていforeach
ますが、C#のループを逆方向にループできますか?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
どのようにそれを逆転しますか?
for
ステートメントを使用しても同じ効果が得られることはわかっていforeach
ますが、C#のループを逆方向にループできますか?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
どのようにそれを逆転しますか?
回答:
リストを使用する場合(直接インデックス付け)、for
ループを使用する場合ほど効率的に行うことはできません。
編集:これは通常、ループを使用できる場合に、for
このタスクに適した方法である可能性が高いことを意味します。さらに、foreach
順序どおりに実装されている限り、構造自体は、要素のインデックスや反復順序に依存しないループを表現するために構築されます。これは、並列プログラミングで特に重要です。私の意見反復は使用してはならないために頼ることをforeach
ループするために。
yield return
" を使用して可能になりました。
.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をフィードすると明らかに爆発します。
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";
}
}
それを逆に反復しようとするとどうなると思いますか?
foreach
反復に使用する前に、次のreverse
方法でリストを逆にします。
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
myList
が役立つかについての注意の言葉。IEnumerable.Reverseはここでは機能しません。
myList
タイプであると思われるSystem.Collections.Generic.List<System.Windows.Documents.List>
(またはその他のカスタムList
P:タイプ)そうでない場合は、このコードは動作しません
インデックスを作成する余裕がない場合や、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
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などの自己説明的なクラス名を選択することもできます)。
いいえ。ForEachは各項目のコレクションを反復処理するだけで、順序はIEnumerableを使用するかGetEnumerator()を使用するかによって異なります。
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)) {
.
.
}
私は働いたこのコードを使用しました
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) {
.Reverse()
は実際にリストを変更しているため、これは悪いことです。
これはかなりうまくいきます
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]);
}