LINQ保证订购SelectMany?

时间:2015-08-31 15:47:32

标签: c# linq

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

例如{{1,2,3},{4,5},{6}} =&gt; {1,2,3,4,5,6}

我可以通过IOrderedEnumerable<T> bar = foo.SelectMany(x => x);执行此操作,还是LINQ无法保证在展平时如何处理订单?

2 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:6)

列表(由.net中的IEnumerable<T>表示)以及两个操作组成一个monad,必须遵守monad laws。这两个操作在不同的语言中被赋予不同的名称,维基百科文章使用Haskell,称之为return>>=(称为&#39; bind&#39;)。 C#调用>>= SelectMany并且没有return的内置函数。这些名称并不重要,重要的是类型。专门针对IEnumerable<T>的是:

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

Return只返回包含给定元素的1元素序列,例如

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#中的前两个monad法则:

左侧身份

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

不符合所要求的权利身份法。