13

ご存知のとおりEnumerable.SelectMany、シーケンスのシーケンスを1つのシーケンスにフラット化します。シーケンスのシーケンスなどを再帰的にフラット化できるメソッドが必要な場合はどうなりますか?

私はすぐに、つまり熱心に評価された実装を思いつきましたが、たとえばキーワードICollection<T>を使用して遅延評価されたものを作成する方法についてはまだ頭を悩ませています。yield

static List<T> Flatten<T>(IEnumerable list)  {
    var rv = new List<T>();
    InnerFlatten(list, rv);
    return rv;
}

static void InnerFlatten<T>(IEnumerable list, ICollection<T> acc) {
    foreach (var elem in list) {
        var collection = elem as IEnumerable;
        if (collection != null) {
            InnerFlatten(collection, acc);
        }
        else {
            acc.Add((T)elem);
        }
    }
}

何か案は?.NET言語の例を歓迎します。

4

2 に答える 2

18

私があなたの考えを理解した限り、これは私の変種です:

static IEnumerable<T> Flatten<T>(IEnumerable collection)
{
    foreach (var o in collection)
    {
        if (o is IEnumerable && !(o is T))
        {
            foreach (T t in Flatten<T>((IEnumerable)o))
                yield return t;
        }
        else
            yield return (T)o;
    }
}

確認してください

List<object> s = new List<object>
    {
        "1",
        new string[] {"2","3"},
        "4",
        new object[] {new string[] {"5","6"},new string[] {"7","8"},},
    };
var fs = Flatten<string>(s);
foreach (string str in fs)
    Console.WriteLine(str);
Console.ReadLine();

明らかに、いくつかの型妥当性チェックが欠けています(InvalidCastExcpetionifコレクションに含まれていない場合T、およびおそらく他のいくつかの欠点)...まあ、少なくとも、必要に応じて遅延評価されます。

!(o is T)アレイstringへの平坦化を防ぐために追加されましたchar

于 2012-11-16T02:11:26.383 に答える
6

これは、再帰的なシーケンス式を使用するF#では簡単です。

let rec flatten (items: IEnumerable) =
  seq {
    for x in items do
      match x with
      | :? 'T as v -> yield v
      | :? IEnumerable as e -> yield! flatten e
      | _ -> failwithf "Expected IEnumerable or %A" typeof<'T>
  }

テスト:

// forces 'T list to obj list
let (!) (l: obj list) = l
let y = ![["1";"2"];"3";[!["4";["5"];["6"]];["7"]];"8"]
let z : string list = flatten y |> Seq.toList
// val z : string list = ["1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"]
于 2012-11-16T02:52:08.833 に答える