此問題屬於F# Set using custom class的下一級 - 我想爲通用接口定義IComparable。如何在F#中抽象(接口)類型中集中定義IComparable?
我有一組實現共享元數據交換接口的類型的任意集合,ITree
。我想比較這些類型,僅使用ITree
中的公開數據。我意識到這個東西並不完全是慣用的F#,但我試圖與現有的C#和VB代碼互操作,所以我想在可能的情況下使用.NET接口和比較。
open System
open System.Collections.Generic
// Simplified "generic" metadata type that all implementers agree on
type ITree =
abstract Path: string with get, set
abstract ModifyDate: DateTime with get, set
type Thing1(path, date) =
interface ITree with
member x.Path = path
member x.ModifyDate = date
// In reality, the types implementing ITree are going to
// come from different external assemblies
type Thing2(path, date) =
interface ITree with
member x.Path = path
member x.ModifyDate = date
let d1 = DateTime.Now
let d2 = DateTime.Now.AddMinutes(-2.0)
let xs : seq<ITree> = Seq.cast [ Thing1("/stuff", d1); Thing1("/dupe", d1); Thing1("/dupe", d1) ]
let ys : seq<ITree> = Seq.cast [ Thing2("/stuff", d2); Thing2("/dupe", d1) ]
// Then I would like to take advantage of F# Sets
// to do comparison across these things
let xset = Set.ofSeq xs
let yset = Set.ofSeq ys
let same = Set.intersect xset yset
let diffs = (xset + yset) - same
現在實際的問題:這並不編譯,因爲ITree
還沒有實現IComparable
。我需要一個自定義的比較來幫助解決時鐘偏移問題,最終還有其他一些問題
有沒有一種方法可以定義比較函數ITree
直接這樣所有其他程序集都不需要考慮它,只需提供它們的數據?
如果我嘗試做
type ITree =
abstract Path: string with get, set
abstract ModifyDate: DateTime with get, set
interface IComparable<ITree> with
let Subtract (this: ITree) (that: ITree) =
this.ModifyDate.Subtract(that.ModifyDate)
match compare (this.Path, this.ParentPath) (that.Path, this.ParentPath) with
| 0 ->
// Paths are identical, so now for a stupid timespan comparison
match abs (Subtract this that).TotalSeconds with
| x when x > 60.0 -> int x
| _ -> 0
| x -> x
編譯器認爲ITree
不再是一個抽象的接口,或一些令人困惑。
現在,我可以創建一個所有實現者必須共享的基類型,但我不想這樣做,因爲這些其他類型實際上只需要在此接口上公開其數據,它們已經存在,並且可能由於其他原因已經有了一個基類。
也許我可以用IComparer<T>
,像
type ITreeComparer =
interface IComparer<ITree> with
member x.Compare(this, that) = ...
但我不知道如何告訴Set...
功能使用的IComparer。
(我認爲,一旦我弄清楚如何申請IComparer<T>
,同樣的方法將用於IEqualityComparer<T>
根據需要使用。)
編輯:我可以做
let x = new HashSet<ITree>(a |> Seq.cast<ITree>, new ITreeEqualityComparer())
要使用正常的.NET集合,對於這個問題應該足夠好;但是,我仍然想知道是否有更好的方法來做我想做的事情。
不錯,那是我需要做的。我剛開始嘗試使用HashSet和man,簽名是可怕的... UnionWith(seq ):unit - auuuugh!就地修改! –
2009-11-23 17:28:23
這很好。沒有難看的代碼重複。現在我只希望我足夠聰明,想到這一切都在我寂寞的... – 2009-11-23 18:59:00