【问题标题】:Why F#'s default set collection is sorted while C#'s isn't?为什么 F# 的默认集合是排序的,而 C# 不是?
【发布时间】:2019-08-07 11:37:20
【问题描述】:

当从 C# 世界迁移到 F#(最惯用的)思维方式时,我发现了这个有趣的区别。

在C#的OOP&mutable世界中,默认的集合集合似乎是HashSet,默认情况下似乎没有排序(因为它接受的比较器只是为了相等);而如果你想要一个排序的,你必须使用SortedSet

但是在 F# 的世界中,基本的 set 已经排序,因为它需要用于实现相等比较的元素类型。这有什么具体原因吗?为什么在该语言的主集合中没有无序集?

作为旁注,我想知道是否有可能拥有一个不允许重复的集合,但在将某些元素作为重复项丢弃时优先于某些元素。示例:一条记录​​ { Name: string; Flag: Option<unit> },因此当插入 { Name = "foo"; Flag = None } 和后来的 { Name = "foo"; Flag = Some() } 时,它最终只包含后一个元素(因为存在 Flag)。

【问题讨论】:

  • C# 没有默认集合。 HashSet 更常见只是因为不需要排序的场景更常见——查找、添加、集合操作。在这些情况下,HashSet 的性能是 O(1),而 SortedSet 和 F# 的集合是 O(logN)
  • stackoverflow.com/questions/16216439/…stackoverflow.com/questions/28420373/… 可能值得一读。这里的根本区别似乎是可变性 - HashSet 被设计为可变的,因此它的实现针对该场景进行了优化。 F# 没有。
  • @knocte Petricek 的第一个项目符号是实际答案 - HashSet 在内部使用缓冲区,就像列表一样,所以“删除”或添加项目是快速只要你不'不必重新分配缓冲区 - 只需将某些内容写入空闲位置,或“取消标记”它。生成 new (hash)set 的集合操作可能比走两棵树更昂贵,就像 F# 的 set 或 SortedSet 所做的那样。检查例如 F# 的 intersect 实现,intersectAux。本质上,它同时在两棵树上行走
  • 我认为错误是用.net 标签标记问题;如果你看一下最接近的选民,他们中的大多数是 C# 人;而一般来说,当我提出 F# 问题并且只标记 f# 时,我的大多数问题都非常受欢迎,并且我得到了大量高质量的回复;我向 F# 社区致敬

标签: c# .net f# set c#-to-f#


【解决方案1】:

F# Set 恰好是排序的,但它更多的是由底层数据结构的选择产生的实现细节,通常不应依赖。

F# 集和映射基于 AVL 树的变体,并且该结构恰好保持存储在树中的元素被排序的不变性。它需要比较约束的原因是因为在这个树结构中的查找依赖于元素之间的直接比较来选择被遍历的子树。

然而,这些结构的卖点在于,它们可以用于实现相当高效、不可变的地图和集合版本,而且价格低廉,而这正是 F# 在更广泛的 .NET 平台不提供任何功能的时候所需要的。替代品。

请注意,在这种情况下,这不是唯一可行的选择,像 Clojure 或 Scala 这样的 JVM 函数式语言选择了不同的数据结构作为其映射的基础 - 哈希数组映射的 trie - 这也是不可变和持久的,可以说更多实现起来很复杂,可以说对于更大的集合大小更有效,但碰巧存储无序的元素。与 AVL 树不同,树的遍历是基于散列的,因此不需要比较约束。

因此,如果您已经知道您的优先级是不变性,那么排序集实际上比未排序集更容易实现。

【讨论】:

    猜你喜欢
    • 2020-10-27
    • 1970-01-01
    • 2015-09-09
    • 2011-10-09
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多