是否可以实现递归“SelectMany”?

众所周知, Enumerable.SelectMany将一系列序列展平为单个序列。 如果我们想要一种能够使序列序列序列变平,等等递归的方法怎么办?

我很快就得到了一个使用ICollection ,即热切评估,但我仍然在考虑如何使用yield关键字制作一个懒惰评估的实现。

 static List Flatten(IEnumerable list) { var rv = new List(); InnerFlatten(list, rv); return rv; } static void InnerFlatten(IEnumerable list, ICollection acc) { foreach (var elem in list) { var collection = elem as IEnumerable; if (collection != null) { InnerFlatten(collection, acc); } else { acc.Add((T)elem); } } } 

有任何想法吗? 任何.NET语言欢迎中的示例。

这在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"] 

据我了解你的想法,这是我的变种:

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

并检查它

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

显然,它确实缺少一些类型有效性检查( InvalidCastExcpetion如果集合不包含T ,可能还有一些其他缺点)……好吧,至少它是懒惰评估的,如期望的那样。

添加了!(o is T)以防止stringchar数组