LINQ保证排序与SelectMany

本文关键字:SelectMany 排序 LINQ | 更新日期: 2023-09-27 18:10:29

我有一个有序枚举值IorderedEnumerable<T>[] foo的数组,我想将它扁平化,以便foo的有序枚举值按照它们存储在数组中的顺序连接在一起。

例如

{{1,2,3},{4、5},{6}}=> {1,2,3,4,5,6}

我可以通过IOrderedEnumerable<T> bar = foo.SelectMany(x => x);来做这个,或者LINQ不保证在平坦时如何处理顺序?

LINQ保证排序与SelectMany

所有LINQ to Objects方法(OrderBy()ToDictionary()除外)将保留源排序。

列表(在。net中由IEnumerable<T>表示)和两个操作组成一个单子,它必须遵守单子法则。这两个操作在不同的语言中有不同的名称,维基百科文章使用Haskell将它们称为return>>=(称为"绑定")。c#调用>>= SelectMany,并且没有return的内置函数。然而,名字并不重要,重要的是类型。专门用于IEnumerable<T>的这些是:

Return :: T -> IEnumerable<T>
SelectMany :: IEnumerable<T> -> Func<T, IEnumerable<U>> -> IEnumerable<U>

Return只是返回一个包含给定元素的单元素序列,例如

public static IEnumerable<T> Return<T>(T item)
{
    return new[] { item };
}

SelectMany已经被实现为Enumerable.SelectMany:

public static IEnumerable<U> SelectMany<T, U>(IEnumerable<T> seq, Func<T, IEnumerable<U>> f) { ... }

SelectMany接受一个输入序列和一个函数,该函数为输入序列中的每一项生成另一个序列,并将结果序列平铺成一个序列。

重申c#中的前两个单子法则,我们有:

左身份

Func<T, IEnumerable<U>> f = ...
Return(x).SelectMany(f) == f(x)

正确的身份

IEnumerable<T> seq = ...
seq.SelectMany(Return) == seq

根据正确的恒等律,SelectMany必须将Func<T, IEnumerable<U>>生成的每一个序列按输入元素的顺序平铺。

假设它以相反的顺序使它们变平,例如

new[] { 1, 2 }.SelectMany(i => new[] { i, -i }) == new[] { 2, -2, 1, -1 }
然后

var s = new[] { 1, 2 }
s.SelectMany(Return) == new[] { 2, 1 } != s