を使用してリストを分割します。基本的には、リストを繰り返し処理し、条件が満たされるまで要素を1つのリストに追加し、残りを別のリストに追加します。は、私は次のコードをしたLINQ
linqを使用して? CheckCondition()は高価であり、リストが巨大になる可能性があるので、リストを2回繰り返すことはしません。
を使用してリストを分割します。基本的には、リストを繰り返し処理し、条件が満たされるまで要素を1つのリストに追加し、残りを別のリストに追加します。は、私は次のコードをしたLINQ
linqを使用して? CheckCondition()は高価であり、リストが巨大になる可能性があるので、リストを2回繰り返すことはしません。
は二度リストを列挙するために起こっているソリューションですが、それは条件をもう一度チェックしませんので、それはより速くする必要があります:
var a = someList.TakeWhile(x => !CheckCondition(x)).ToList();
var b = someList.Skip(a.Count).ToList();
someList
が
IList<T>
の場合、各項目は実際には1回のみ列挙されるため、罰金は発生しません。
それが実際だろう
(これについてJon Skeet's articleを参照)を使用すると、簡単にこの最適化を使用して、独自のSkip
メソッドを実装することができしかし...私はSkip
がIList<T>
の場合のために最適化されていたと思ったが、どうやらそうではありませんよりエレガントなTakeUntil
方法があった場合...私たちはそれを容易に作成することができます。この方法で
public static IEnumerable<TSource> TakeUntil<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
foreach(var item in source)
{
if (predicate(item))
break;
yield return item;
}
}
を、コードは次のようになります。
var a = someList.TakeUntil(CheckCondition).ToList();
var b = someList.Skip(a.Count).ToList();
個人的には、私はここにLINQの必要はないと思います。
私のようなものだろう。ここでは
bool conditionHit = false;
foreach (var item in someList)
{
if (!conditionHit)
conditionHit = CheckCondition(item);
var listToBeAdded = conditionHit ? b : a;
listToBeAdded.Add(item);
}
これは、複数回最初のリスト内のアイテムの上に行くことになりますが、初回のみCheckCondition
を通じて呼び出す:
var a = someList.TakeWhile(e => !CheckCondition(e));
var b = someList.Skip(a.Count());
a.Count()は最初のクエリを列挙しますが、結果を取得するには再び*列挙する必要があります...最後にToListを呼び出す必要があります(そして、私の解決策になります) –
someList
コンクリートList<T>
ある場合、これが唯一のシングルが必要になります
私はAni's answerを変更したくありませんでしたが、ここでは少し単純化しました。
var listToBeAdded = a;
foreach (var item in someList)
{
if (listToBeAdded == a && CheckCondition(item))
listToBeAdded = b;
listToBeAdded.Add(item);
}
+1 - 素晴らしい答え。 – ChaosPandion
次に、この(イテレータを巻き戻すために知られているのLINQのビルトインメソッドを()再利用していない)、私はパフォーマンスであると信じている(OPのロジックを再利用し、それが再評価されない条件を試してみてくださいリストの半分)と、きちんとした拡張メソッドでそれを梱包し、タプル:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Craft
{
class Act
{
static void Main(string[] args)
{
var a = new List<string>
{ "I", "Love", "You", "More", "Today", "Than", "Yesterday" };
var tx = a.SplitByCondition(s => s == "More");
foreach (var s in tx.Item1)
Console.WriteLine("First Half : {0}", s);
foreach (var s in tx.Item2)
Console.WriteLine("Second Half : {0}", s);
Console.ReadLine();
}
}//Act
public static class Helper
{
public static Tuple<List<T>, List<T>> SplitByCondition<T>
(this IEnumerable<T> t, Func<T, bool> terminator)
{
var tx = new Tuple<List<T>, List<T>>
(new List<T>(), new List<T>());
var iter = t.GetEnumerator();
while (iter.MoveNext())
{
if (terminator(iter.Current))
{
tx.Item2.Add(iter.Current);
break;
}
tx.Item1.Add(iter.Current);
}
while (iter.MoveNext())
tx.Item2.Add(iter.Current);
return tx;
}
}//Helper
}//Craft
出力:私はあなたのコードを理解
First Half : I
First Half : Love
First Half : You
Second Half : More
Second Half : Today
Second Half : Than
Second Half : Yesterday
が、 '(e.Current)場合、'意味がありません。私はあなたが最後の 'MoveNext'呼び出しの値を(最初のループから)保存し、それが成功したかどうかを調べることを検討していると思います。 – Ani
ループ外のアイテムを処理するのではなく、CheckConditionがtrueになったら、e.Currentを 'b'に追加するように更新しました。 – Anonym
はい、今は明らかです。 – Ani