Возможно ли реализовать рекурсивный "SelectMany"?
Как мы все знаем, Enumerable.SelectMany
выравнивает последовательность последовательностей в одну последовательность. Что, если мы хотим получить метод, который мог бы сгладить последовательности последовательностей последовательностей и т.д. Рекурсивно?
Я быстро придумал реализацию с использованием 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.
Ответы
Ответ 1
Это тривиально в 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"]
Ответ 2
Насколько я понял вашу идею, это мой вариант:
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();
Очевидно, что ему не хватает проверок валидности типа (InvalidCastExcpetion
, если коллекция содержит не T
и, вероятно, некоторые другие недостатки)... ну, по крайней мере, она лениво оценивается по желанию.
!(o is T)
был добавлен для предотвращения сглаживания массива string
до char