【问题标题】:Does Seq.groupBy preserve order within groups?Seq.groupBy 是否保留组内的顺序?
【发布时间】:2016-12-29 17:08:44
【问题描述】:

我想对一个序列进行分组,然后获取组中每个元素的第一次出现。当我尝试这个时

Seq.groupBy f inSeq
|> Seq.map (fun (k,s) -> (k,s|>Seq.take 1|>Seq.exactlyOne))

我发现有时我会得到与 s 不同的元素。这是预期的吗?

【问题讨论】:

  • 是的,我应该在第二行使用 Seq.head,但我想逐字分享我正在使用的代码。 :)
  • 当这种情况发生时,你能隔离案例吗?

标签: f# sequences


【解决方案1】:

看着source of the groupBy implementation - 这是相关位:

// Build the groupings

seq |> iter (fun v ->
    let safeKey = keyf v
    let mutable prev = Unchecked.defaultof<_>
    match dict.TryGetValue (safeKey, &prev) with
    | true -> prev.Add v
    | false ->
        let prev = ResizeArray ()
        dict.[safeKey] <- prev
        prev.Add v)

它遍历源数组并将值添加到键的相应列表中。子序列的顺序直接受输入序列的顺序影响。对于相同的输入序列,我们可以期望groupBy 返回相同的输出序列。这就是 groupBy 的测试编码方式。

如果您看到结果序列的变化,请检查输入序列。

【讨论】:

  • 谢谢,这是我的预期,但由于某种原因,我收到了一些不同的东西。不幸的是,现在我无法重现该问题。
  • @RobertSim 一定是“Works on My Machine™”的反面案例。
【解决方案2】:

是的,这是意料之中的。序列 (seq) 不能保证为 pure。您可以定义一个序列,每次迭代它们时都会产生不同的值。如果你调用Seq.take 1 两次,你会得到不同的结果。

以这个序列为例:

open System

let r = Random ()
let s = seq { yield r.Next(0, 9) }

如果您为此致电Seq.take 1,您可能会得到不同的结果:

> s |> Seq.take 1;;
val it : seq<int> = seq [4]
> s |> Seq.take 1;;
val it : seq<int> = seq [1]

使用Seq.head 也无济于事:

> s |> Seq.head;;
val it : int = 2
> s |> Seq.head;;
val it : int = 6

如果您想保证确定性行为,请改用List

【讨论】:

  • ... 或者在某些情况下,可以使用Seq.cache 来“固定”seq(但最后一行重新建模应该保留的东西)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 2010-10-14
  • 2017-03-04
  • 1970-01-01
  • 1970-01-01
  • 2021-03-04
相关资源
最近更新 更多