3

次のようなメソッドがあるとします。

IEnumerable<record> GetSomeRecords()
{
  while(...)
  {
    yield return aRecord
  }
}

さて、同じタイプの列挙型も返す呼び出し元があるとしましょう。

IEnumerable<record> ParentGetSomeRecords()
{
  // I want to do this, but for some reason, my brain locks right here
  foreach(item in someItems)
    yield return GetSomeRecords();
}

yield return が型レコードを必要としており、レコードの IEnumerable を返しているため、そのコードは構文エラー エラーを取得します。

列挙型のネストされたループを平坦化する「フラットな」IEnumerable が 1 つ必要です。以前にこれをやったことがあると知っているので、それは私を夢中にさせていますが、それが何であったか思い出せないようです. ヒントはありますか?

4

3 に答える 3

9

これはあなたが求めているものですか?

IEnumerable<record> ParentGetSomeRecords()
{
    foreach(var item in someItems)
        foreach(var record in GetSomeRecords())
            yield return record;
}

前述のように、これは単一レベルの子に対してのみ機能しますが、サンプルコードと最も同等です。

アップデート

一部の人々は、階層構造をフラット化する機能が必要だと信じているようです。幅優先探索を実行する拡張メソッドは次のとおりです(子の前に兄弟を取得します)。

単一のアイテムから来る:

[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this T source, Func<T, IEnumerable<T>> selector)
{
    Contract.Requires(!ReferenceEquals(source, null));
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    var pendingChildren = new List<T> {source};

    while (pendingChildren.Any())
    {
        var localPending = pendingChildren.ToList();
        pendingChildren.Clear();
        foreach (var child in localPending)
        {
            yield return child;
            var results = selector(child);
            if (results != null)
                pendingChildren.AddRange(results);
        }
    }
}

これは次のように使用できます。

record rec = ...;
IEnumerable<record> flattened = rec.BreadthFirstFlatten(r => r.ChildRecords);

これにより、すべてのrecの子、すべての子の子などがIEnumerable<record>含まれるようになります。rec

のコレクションから来ている場合はrecords、次のコードを使用してください。

[Pure]
private static IEnumerable<T> BreadthFirstFlatten<T, TResult>(IEnumerable<T> source, Func<T, TResult> selector, Action<ICollection<T>, TResult> addMethod)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Requires(addMethod != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    var pendingChildren = new List<T>(source);

    while (pendingChildren.Any())
    {
        var localPending = pendingChildren.ToList();
        pendingChildren.Clear();
        foreach (var child in localPending)
        {
            yield return child;
            var results = selector(child);
            if (!ReferenceEquals(results, null))
                addMethod(pendingChildren, results);
        }
    }
}

[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.AddRange(arg2));
}

[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, T> selector)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);

    return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.Add(arg2));
}

これらの2つの拡張方法は、次のように使用できます。

IEnumerable<records> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ChildRecords);

または逆方向から:

IEnumerable<record> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ParentRecords);

これらの拡張メソッドはすべて反復的であるため、スタックサイズによる制限はありません。

事前注文と事後注文の深さ優先探索を含む、これらのタイプのメソッドがたくさんあります。それらを表示したい場合は、リポジトリを作成してどこかにアップロードします:)

于 2013-02-28T21:58:09.887 に答える
2

どうですか:

IEnumerable<record> ParentGetSomeRecords()
{
    var nestedEnumerable = <whatever the heck gets your nested set>;
    // SelectMany with an identity flattens 
    // IEnumerable<IEnumerable<T>> to just IEnumerable<T>
    return nestedEnumerable.SelectMany(rec => rec);
}
于 2013-02-28T21:55:38.200 に答える
0

非効率的ですが、これを使用できます:

List<Record> rcrdList = new List<Record>();
foreach (var item in someItems)
{
    rcrdList.AddRange(item.GetSomeRecords());
}
return rcrdList;
于 2013-02-28T21:58:01.813 に答える