5

LINQ を使用して、C# のシーケンスをシーケンスのシーケンスに分割したいと思います。私はいくつかの調査を行いましたが、わずかに関連していることがわかった最も近い SO 記事はthisです。

ただし、この質問は、定数値に基づいて元のシーケンスを分割する方法を尋ねているだけです。操作に基づいてシーケンスを分割したいと思います。

具体的には、小数プロパティを含むオブジェクトのリストがあります。

public class ExampleClass
{
    public decimal TheValue { get; set; }
}

のシーケンスがExampleClassあり、対応する の値のシーケンスTheValueは次のとおりです。

{0,1,2,3,1,1,4,6,7,0,1,0,2,3,5,7,6,5,4,3,2,1}

元のシーケンスを、似IEnumerable<IEnumerable<ExampleClass>>ている値を持つに分割したいと思います。TheValue

{{0,1,2,3}, {1,1,4,6,7}, {0,1}, {0,2,3,5,7}, {6,5,4,3,2,1}}

これがどのように実装されるのか、私はただ迷っています。お手伝いできますか?

私は今、非常に醜い解決策を持っていますが、LINQ が私のコードの優雅さを増すという「感覚」を持っています。

4

3 に答える 3

1

あるいは、linq 演算子と参照による .net クロージャーの悪用を使用します。

public static IEnumerable<IEnumerable<T>> Monotonic<T>(this IEnumerable<T> enumerable)
{
  var comparator = Comparer<T>.Default;
  int i = 0;
  T last = default(T);
  return enumerable.GroupBy((value) => { i = comparator.Compare(value, last) > 0 ? i : i+1; last = value; return i; }).Select((group) => group.Select((_) => _));
}

ロギング用に IEnumerable をその場しのぎのテーブルに分割するためのランダムなユーティリティ コードから取得しました。私の記憶が正しければ、奇妙な末尾の Select は、入力が文字列の列挙である場合のあいまいさを防ぐためのものです。

于 2011-07-11T20:03:14.103 に答える
1

これは、ほぼすべての基準に従ってシーケンスを分割するカスタム LINQ 演算子です。パラメータは次のとおりです。

  1. xs: 入力要素シーケンス。
  2. func: 「現在の」入力要素と状態オブジェクトを受け取り、タプルとして返す関数:
    • bool入力シーケンスを「現在の」要素の前に分割する必要があるかどうかを示します。と
    • の次の呼び出しに渡される状態オブジェクトfunc
  3. initialStatefunc:最初の呼び出しで渡される状態オブジェクト。

これは、ヘルパー クラスと一緒です (yield return明らかにネストできないため必要です)。

public static IEnumerable<IEnumerable<T>> Split<T, TState>(
                  this IEnumerable<T> xs,
                  Func<T, TState, Tuple<bool, TState>> func, 
                  TState initialState)
{
    using (var splitter = new Splitter<T, TState>(xs, func, initialState))
    {
        while (splitter.HasNext)
        {
            yield return splitter.GetNext();
        }
    }
}
internal sealed class Splitter<T, TState> : IDisposable
{
    public Splitter(IEnumerable<T> xs, 
                    Func<T, TState, Tuple<bool, TState>> func, 
                    TState initialState)
    {
        this.xs = xs.GetEnumerator();
        this.func = func;
        this.state = initialState;
        this.hasNext = this.xs.MoveNext();
    }

    private readonly IEnumerator<T> xs;
    private readonly Func<T, TState, Tuple<bool, TState>> func;
    private bool hasNext;
    private TState state;

    public bool HasNext { get { return hasNext; } }

    public IEnumerable<T> GetNext()
    {
        while (hasNext)
        {
            Tuple<bool, TState> decision = func(xs.Current, state);
            state = decision.Item2;
            if (decision.Item1) yield break;
            yield return xs.Current;
            hasNext = xs.MoveNext();
        }
    }

    public void Dispose() { xs.Dispose(); }
}

注:Split以下は、メソッドに適用された設計上の決定事項の一部です。

  • シーケンスを 1 回だけパスする必要があります。
  • 状態を明示的にすることで、副作用を防ぐことができfuncます。
于 2013-01-23T22:46:49.057 に答える