groupBy
是否保证在以下代码中保留排序顺序?
x
|> Seq.sortBy (fun (x, y) -> y)
|> Seq.groupBy (fun (x, y) -> x)
通过保留排序顺序,我的意思是我们可以保证在x
的每个分组中,结果仍按y
排序。
这对于简单的例子来说是正确的,
[(1, 3);(2, 1);(1, 1);(2, 3)]
|> Seq.sortBy (fun (x, y) -> y)
|> Seq.groupBy (fun (x, y) -> x)
// seq [(2, seq [(2, 1); (2, 3)]); (1, seq [(1, 1); (1, 3)])]
我想确保没有奇怪的边缘情况。
答案 0 :(得分:8)
保留排序顺序是什么意思? Seq.groupBy
更改了序列的类型,那么你怎么能在和 之后有意义地比较?
对于xs
类型的给定seq<'a * 'b>
,表达式xs |> Seq.sortBy snd
的类型为seq<'a * 'b>
,而表达式xs |> Seq.sortBy snd |> Seq.groupBy fst
的类型为{ {1}}。因此,问题的答案是是还是否取决于保留排序顺序的含义。
正如@Petr在评论中写道的那样,测试它很容易。如果您担心特殊情况,请使用FsCheck撰写属性,看看它是否有所说明:
seq<'a * seq<'a * 'b>>
在此属性中,我将排序顺序保留的属性解释为,如果随后连接分组序列,则会保留排序顺序。你的定义可能不同。
鉴于这个特定的定义,运行该属性清楚地表明该属性并不成立:
open FsCheck.Xunit
open Swensen.Unquote
[<Property>]
let isSortOrderPreserved (xs : (int * int) list) =
let actual = xs |> Seq.sortBy snd |> Seq.groupBy fst
let expected = xs |> Seq.sortBy snd |> Seq.toList
expected =! (actual |> Seq.map snd |> Seq.concat |> Seq.toList)
我们在此处看到,如果输入为Falsifiable, after 6 tests (13 shrinks) (StdGen (1448745695,296088811)):
Original:
[(-3, -7); (4, -7); (4, 0); (-4, 0); (-4, 7); (3, 7); (3, -1); (-5, -1)]
Shrunk:
[(3, 1); (3, 0); (0, 0)]
---- Swensen.Unquote.AssertionFailedException : Test failed:
[(3, 0); (0, 0); (3, 1)] = [(3, 0); (3, 1); (0, 0)]
false
,则分组序列不会保留排序顺序(这对我来说并不令人惊讶)。
根据更新的问题,这里有一个检查该问题的属性:
[(3, 1); (3, 0); (0, 0)]
这个属性的确坚持:
[<Property(MaxTest = 10000)>]
let isSortOrderPreservedWithEachGroup (xs : (int * int) list) =
let actual = xs |> Seq.sortBy snd |> Seq.groupBy fst
let expected =
actual
|> Seq.map (fun (k, vals) -> k, vals |> Seq.sort |> Seq.toList)
|> Seq.toList
expected =!
(actual |> Seq.map (fun (k, vals) -> k, Seq.toList vals) |> Seq.toList)
您仍应仔细考虑是否要依赖未记录的行为,因为它可能会在以后的F#版本中发生变化。就个人而言,我采纳了the Zen of Python的建议:
BTW,所有转换为F#列表的原因是因为列表具有结构上的相同性,而序列则不具备。明确比隐含更好。
答案 1 :(得分:1)
文档没有明确说明(通过示例除外),但是the implementation does preserve the order of the original sequence。如果它没有:我所知道的其他语言中的等效函数,那将是非常令人惊讶的。
答案 2 :(得分:0)
谁在乎呢。而不是排序然后分组,只需分组然后排序,即使groupBy的F#实现最终改变,也能保证排序:
Virtual core keyboard