0

私は F# について少し読んでいて、試してみることにしました。少し複雑な例から始めて、思いついたのですが、すぐに迷子になりました。誰かがそれについていくつかの考えを共有できるかどうか疑問に思います.

ComparisonStrategy<'T>のインスタンスを返すというメソッドを書きたかったのIEqualityComparer<'T>です。可変長のComparisonWhichAndHow<'T>インスタンスを取り込みます。タイプComparisonWhichAndHow<'T>は次のいずれかです。

  1. type の 1 つの関数。('T -> *)比較する単一のフィールドを選択するメソッドです。
  2. デフォルトまたはで使用し('T -> 'U, IEqualityComparer<'U>)たくない場合の 2 タプル。EqualsGetHashCode'U

これをしばらくビジュアルスタジオで書き込もうとしましたが、関数宣言の部分さえ正しくできません。これを乗り越えることができれば、メソッド本体を実装できると確信していますが、できないようです。

編集:

これは私がこれまでに試したコードです。

私は次の2つのことを達成しようとしています。

  1. オブジェクトごとに equal メソッドを生成する一般的な方法を考え出します。
  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

この問題を別の観点から見ると、(指定した) 2 つの異なる方法で比較を実行するオブジェクトを作成し、それらを構成できるようにしたいようです。

まず、比較を実行するオブジェクトを作成する 2 つの方法を見てみましょう。で両方を表すことができます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 -> 'UF# ジェネリックを使用しているため、任意の型のフィールドを射影できます (型は比較可能である必要があります)。2 番目のプリミティブ関数もを使用しますが、デフォルトを使用する代わりに値'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'

ここで、上記の 2 つの方法のいずれかで作成された一連の値を取得して、単一の比較戦略を構築したいと言っています。それが何を意味するのか完全にはわかりません。指定されたすべての比較子が等しいと報告したときに、2 つのオブジェクトを等しくしたいですか?

その場合、次のように、2 つの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、次のように comparer を構築できます。

// 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 に答える