F#设置不返回正确的差异。可能是我的CompareTo实现的一个问题

时间:2015-07-09 20:06:22

标签: f#

我从两组开始,abc和zxc。 "未修饰的"具有正确数量(1)的项目。
设置abc有两个项目,其中一个是未修改的项目

let unmodified = Set.intersect abc zxc
let newAndModified = a - unmodified

我希望newAndModified包含一个项目,但它有两个。它似乎与abc相同。

即使我的Set.intersect工作正常,其他Set函数也没有返回正确的结果,我相信我为这个对象实现CompareTo的方式有问题。

type Bar =
{ Id : int
  Name : string
}

[<CustomEquality; CustomComparison>]
type Foo =
 { Name : string
   Id   : int
   Bars : Bar list
 }

 override x.Equals(y) =
   match y with
   | :? Foo as y -> x.Id.Equals y.Id
                    && x.Name.Equals y.Name
                    && x.Bars.Head.Name.Equals y.Bars.Head.Name
   | _ -> invalidArg "y" "cannot compare object to one that is not a Foo"

member x.CompareTo (y: obj) =
  match y with
  | null -> nullArg "y"
  | :? Foo as y -> x.CompareTo(y)
  | _ -> invalidArg "y" "Must be an instance of Foo"

member x.CompareTo (y: Foo) =
  match x.Equals y with
  | true -> 0
  | false -> -1

interface IComparable<Foo> with
  member x.CompareTo y = x.CompareTo y

interface System.IComparable with
  member x.CompareTo y =
    match y with
    | :? Foo as y -> (x:> IComparable<_>).CompareTo y
    | _ -> invalidArg "y" "cannot compare values of different types"

1 个答案:

答案 0 :(得分:4)

您的CompareTo实施绝对无法在此处运作。当两个对象相等(这是好的)时返回0,但如果它们不相等,则总是返回-1。这意味着第一个小于最后一个。

但是,这不会起作用。如果您有对象ab,那么您的比较就是a < bb < a(这违反了订购关系的要求)。

F#set要求对象是可订购的,因为它将数据保存在平衡树中(较小的元素位于左侧,较大的元素位于右侧) - 因此CompareTo始终返回-1 ,它最终会创建一个毫无意义的树。

为什么要为该类型实现自定义比较?我想你这样做只是考虑一些领域而忽略其他领域。在这种情况下,您可以做一个很好的技巧,即创建一个包含您感兴趣的字段的元组,并在其上调用内置的hashcompare函数。例如:

compare (x.Id, x.Name, x.Bars.Head.Name)
        (y.Id, y.Name, y.Bars.Head.Name)

这将使用元组的默认F#比较,这是一个正确定义的排序关系。