2010-04-03 156 views
12

有沒有什麼方法可以覆蓋F#集合中的比較函數?F#集合的覆蓋比較

我沒有看到採取IComparer<T>或比較功能的任何組構造函數:

  • Set.ofSeq等不採取比較功能
  • FSharpSet(IComparer<T> comparer, SetTree<T> tree)構造是內部的,因爲
  • SetTree是內部的,
  • SetTreeModule.ofSeq<a>(IComparer<a> comparer, IEnumerable<a> c)顯然也是內部的。

我的實際問題是,我有一組('a * 'a),我想的比較,使得,例如(1,3)=(3,1)。

我知道我可以把這個包裝成一個類型實現IComparable<T>,但有沒有什麼辦法可以避免這種情況?

+0

剛剛重溫這個問題:這是一個可怕的想法。創建一個代表你想要的行爲的新類型。 – 2014-11-01 19:06:59

回答

17

我認爲在F#核心庫中可用的set類型不允許指定您自己的比較。但是,PowerPack中有一個類型爲版本,允許這樣做。下面顯示瞭如何建立一套與比較器,用於比較使用模10的整數:

#r @"FSharp.PowerPack.dll" 
open System.Collections.Generic 
open Microsoft.FSharp.Collections.Tagged 

type MyComparer() = 
    interface IComparer<int> with 
    member x.Compare(a, b) = (a % 10).CompareTo(b % 10) 

// Type alias for a set that uses 'MyComparer' 
type MySet = Tagged.Set<int, MyComparer> 

let comparer = new MyComparer() 
let s1 = MySet.Create(comparer, [1; 2; 3]) 
let s2 = MySet.Create(comparer, [11; 14]) 

MySet.Union(s1, s2) 
4

你需要你的建議使用「包裝」類型。

(對於不可改變的類型,如F#SetMap,還有自定義比較API的問題。由於每個操作返回,例如一個新的Set對象,新的對象需要共享的比較器(可能導致線程問題),並有如果Set.union與不同的比較器的結果不同,SetMap可以避免混淆,因爲總是直接對元素類型進行比較,正如Tomas所說的那樣,PowerPack具有例如,使得比較器部分爲類型的替代API,其使得類型安全/比較安全的方法成爲可能)。

0

我有類似的問題。我也被限制爲僅使用標準庫並且沒有外部依賴性。同時我想利用已有的IEqualityComparerIComparer接口的現有實現。

所以,我最終作出我自己的包裝結構,我現在可以使用F#的內置地圖和設置:

[<Struct>] 
[<CustomComparison>] 
[<CustomEquality>] 
type ComparisonAdapter<'T>(value: 'T, comparer: IComparer<'T>, eqComparer: IEqualityComparer<'T>) = 
    new(value) = ComparisonAdapter(value, Comparer<'T>.Default, EqualityComparer<'T>.Default) 
    member this.CompareTo (cmp: IComparer<'T>, v: 'T) = cmp.Compare(v, value) 
    member this.CompareTo (v: 'T) = this.CompareTo(comparer, v) 
    member this.CompareTo (c: ComparisonAdapter<'T>) = c.CompareTo(comparer, value) 
    member this.CompareTo (o: obj) = 
     if (o :? Comparison<'T>) then this.CompareTo(downcast o: ComparisonAdapter<'T>) 
     else if (o :? 'T) then this.CompareTo(downcast o: 'T) 
     else if (o :? IComparable) then ((downcast o: IComparable)).CompareTo(value) 
     else raise (NotSupportedException()) 
    member this.Equals (c: ComparisonAdapter<'T>): bool = c.Equals(eqComparer, value) 
    member this.Equals (cmp: IEqualityComparer<'T>, v: 'T): bool = cmp.Equals(v, value) 
    member this.Equals (v: 'T): bool = eqComparer.Equals(v, value) 
    override this.Equals (o: obj): bool = 
     if (o :? Comparison<'T>) then this.Equals(downcast o: ComparisonAdapter<'T>) 
     else if (o :? 'T) then this.Equals(downcast o: 'T) 
     else false 
    override this.GetHashCode() = eqComparer.GetHashCode value 
    member this.Value with get() = value 
    interface IEquatable<'T> with member this.Equals other = this.Equals(eqComparer, other) 
    interface IComparable<'T> with member this.CompareTo other = this.CompareTo(comparer, other) 
    interface IComparable with member this.CompareTo o = this.CompareTo o 

我用它來創建內置的包裝方式在收集和內部創建密鑰/項目包裝,以使用戶代碼更友好。所以我會有類似

type MyCustomMap<'TKey, 'TValue>(cmp: IComparer<'TKey>, eq: IEqualityComparer<'TKey>) = 
    let innerMap = new Map<ComparisonAdapter<'TKey>, 'TValue>() 
    member this Add key value = 
     let actualKey = new ComparisonAdapter<'TKey>(key, cmp, eq) 
     innerMap.Add actualKey value