0

我一直在阅读 F# 并决定尝试一下。我从一个有点涉及的例子开始,我想出了并立即迷路了。我想知道是否有人可以分享一些想法。

我想编写一个名为的方法,该方法ComparisonStrategy<'T>返回一个IEqualityComparer<'T>. 它接受可变长度的ComparisonWhichAndHow<'T>实例。类型ComparisonWhichAndHow<'T>可以是:

  1. type 的一个函数('T -> *),它是一种选择单个字段进行比较的方法
  2. 一个 2 元组,('T -> 'U, IEqualityComparer<'U>)如果您不希望使用默认值EqualsGetHashCode用于'U.

我已经尝试在 Visual Studio 上画了一段时间,但我什至无法正确地获得函数声明部分。我有点肯定,如果我能克服这个问题,我将能够实现方法体,但似乎我做不到。

编辑:

这是我到目前为止尝试过的代码。

我正在努力实现以下两件事。

  1. 想出一种为每个对象生成相等方法的通用方法。
  2. 有时某些业务操作可能需要比较 2 个对象的某些字段,以及它们的子对象的某些字段。不是一个完整的比较。我正在尝试使编写这些代码更简洁

这是我到目前为止所拥有的:

module Failed =
    open System.Collections.Generic
    open System

    type ComparsionOption<'T, 'U> =
        | Compare of ('T -> 'U)
        | CompareWith of ('T -> 'U) * IEqualityComparer<'U>

    // TO USE: [<ParamArray>] 
    // TODO: this method returns a dummy for now
    let CompareStrategy (opts : ComparsionOption<'T, _> array) =
        EqualityComparer<'T>.Default

    // How it's used

    type Person(name : string, id : Guid) = 
        member this.Name = name
        member this.ID = id

    let fullCompare : EqualityComparer<Person> =
        CompareStrategy [|Compare(fun (p : Person) -> p.Name);
                            CompareWith((fun (p : Person) -> p.ID), EqualityComparer<Guid>.Default)|] // error here
4

1 回答 1

1

从另一个角度来看问题,您似乎希望能够构建以两种不同方式(您指定)执行比较的对象,然后组合它们。

让我们首先看看构建执行比较的对象的两种方法。您可以用 表示两者IEqualityComparer<'T>。第一个接受一个函数'T -> Something并对结果进行比较。你可以像这样定义一个函数:

/// Creates a comparer for 'T values based on a predicate that 
/// selects some value 'U from any 'T value (e.g. a field)
let standardComparer (f:'T -> 'U) = 
  { new IEqualityComparer<'T> with
      member x.Equals(a, b) = 
        (f a).Equals(b)  // Call 'f' on the value & test equality of results
      member x.GetHashCode(a) = 
        (f a).GetHashCode() } // Call 'f' and get hash code of the result

该函数'T -> 'U使用 F# 泛型,因此您可以投影任何类型的字段(该类型必须具有可比性)。第二个原始函数也采用'T -> 'U,但它也采用值比较器'U而不是使用默认值:

/// Creates a comparer for 'T values based on a predicate & comparer
let equalityComparer (f:'T -> 'U) (comparer:IEqualityComparer<'U>) = 
  { new IEqualityComparer<'T> with
      member x.Equals(a, b) = 
        comparer.Equals(f a, f b) // Project values using 'f' and use 'comparer'
      member x.GetHashCode(a) =
        comparer.GetHashCode(f a) } // Similar - use 'f' and 'comparer'

现在您说您想采用以上述两种方式之一创建的一系列值来构建一个单一的比较策略。我不完全确定你的意思。当所有指定的比较器报告它们相等时,您是否希望两个对象相等?

假设是这种情况,您可以编写一个组合两个IEqualityComparer<'T>值的函数,并在两个比较器报告它们相等时将它们报告为相等,如下所示:

/// Creates a new IEqualityComparer that is based on two other comparers
/// Two objects are equal if they are equal using both comparers.
let combineComparers (comp1:IEqualityComparer<'T>) (comp2:IEqualityComparer<'T>) =
  { new IEqualityComparer<'T> with
      member x.Equals(a, b) =
        comp1.Equals(a, b) && comp2.Equals(a, b) // Combine results using &&
      member x.GetHashCode(a) =
        // Get hash code of a tuple composed by two hash codes
        hash (comp1.GetHashCode(a), comp2.GetHashCode(a)) }

这实际上实现了您需要的所有功能。如果你有一些 object Person,你可以像这样构造比较器:

// Create a list of primitive comparers that compare 
// Name, Age and ID using special 'idComparer'
let comparers =
  [ standardComparer (fun (p:Person) -> p.Name);
    standardComparer (fun (p:Person) -> p.Age);
    equalityComparer (fun (p:Person) -> p.ID) idComparer ]

// Create a single comparer that combines all of them...
let comparePerson = comparers |> Seq.reduce combineComparers

您可以使用重载方法等将其包装在更加面向对象的接口中,但我认为上面的示例显示了您在解决方案中需要的所有重要组件。

顺便说一句:在示例中,我使用F# 对象表达式来实现所有功能。

于 2012-10-21T21:27:20.490 回答