2012-03-01 36 views
1

爲了引用一些與鍵的座標我想使用區分的聯合類型,因爲它們允許各種高效的模式匹配。爲區分的聯合類型實現快速的CustomEquality和CustomComparison

考慮follwoing一小段代碼片段:

[<CustomEquality; CustomComparison>] 
type Coord = 
| Spot of AssetKey 
| Vol of AssetKey * DateTime option 
| Rate of Currency         
..... 

    member this.sortKey = 
     match this with 
     | Spot(key)        -> (0 , key.toString) 
     | Vol(key)        -> (1 , key.toString) 
     | Vol(key, Some(t))      -> (2 , key.toString + t.ToShortString()) 
     | Rate(cur)        -> (3 , cur.toString) 
     ...... 

    interface IComparable with 
     member this.CompareTo(obj) = 
      match obj with 
      | :? Coord as other -> compare this.sortKey other.sortKey 
      | _ -> invalidArg "obj" "not a Coord type" 

    override this.Equals(obj) = 
      match obj with 
      | :? Coord as other -> this.sortKey = other.sortKey 
      | _ -> false 

    override this.GetHashCode() = this.sortKey.GetHashCode() 

我需要強制執行特定的排序順序。例如Spot < Vol總是。我用sortKey成員函數強制執行。

的AssetKey是再次非常類似區分聯合類型:

[<StructuralEqualityAttribute; StructuralComparisonAttribute>] 
type AssetKey = 
| Equity of string 
| EquityIndex of string 
..... 

所以這一切都工作得很好,但它是緩慢的。據我所知,如果調用sortKey函數,整個鍵就會重新構建,特別是toString函數會再次被調用。

一個明顯的改進是添加一個緩存層,這比解決方案更像是一種破解。

進一步的優化是在字符串上使用散列鍵。但是在這裏我需要添加緩存,因爲我需要緩存散列鍵,我不想重新計算它。

的性能優化會更容易,如果我使用一個結構或類,但然後我鬆模式匹配等的靈活性例如

match c with 
| Coord.Vol(ak, _) when ak = assetKey -> true 
| _ -> false 

將是什麼的另一種方法,這是很好執行?在我的一些定時中,sortKey函數中有30%以上的整體性能丟失。

感謝您的任何建議和改進。

回答

2

簡單的優化
一個基本的優化,你可以很容易做的是避免調用toString時,您可以根據剛上的Coord類型的決定。相反建設sortKey的你可以寫:

// Separate functions that return tag and key, so that we don't 
// have to call 'toString' if we can decide based just on the Tag 
member this.Tag = 
    match this with 
    | Spot _ -> 0 | Vol(_, None) -> 1 
    | Vol _ -> 2 | Rate _ -> 3 
member this.Key = 
    match this with 
    | Spot(key) | Vol(key, None) -> key.toString | Rate cur -> cur.toString 
    | Vol(key, Some t) -> key.toString + t.ToShortString()) 

interface IComparable with 
    member this.CompareTo(obj) = 
     match obj with 
     | :? Coord as other -> 
      let c = compare this.Tag other.Tag 
      // Try comparing based on the tag first - if the tags 
      // are the same, then get Key and compare based on the key 
      if c <> 0 then c else compare this.Key other.Key 
     | _ -> invalidArg "obj" "not a Coord type" 

如果你想緩存的toString的結果,那麼你需要使用一些結構,它允許你存儲本地領域。我可能會使用一個對象類型(表示爲一個類或簡單的結構)。

包纏式
在這種情況下,你仍然可以得到很好的模式採用有源相匹配的模式,但它需要定義每類活動模式(這可能不是那麼糟糕)。這裏有一個例子:

// This type will not be used directly - it is an internal implementation 
// hidden from the users that will be accessed using active patterns 
[<RequiresQualifiedAccess>] 
type AssetKeyInternal =  
    | Equity of string  
    | EquityIndex of string 
    override x.ToString() = ... 

// Public type with active patterns for pattern matching 
type AssetKey(key:AssteKeyInternal) = 
    let str = lazy key.ToString() // Lazily cached string 
    member x.Key = str.Value  // Evaluated when accessed for the first time 

    member x.Value = key // Returns the internal representation 

// Define active patterns working over AssetKey type 
let (|Equity|EquityIndex|) (k:AssetKey) = 
    match k.Value with 
    | AssetKeyInternal.Equity(e) -> Equity(e) 
    | AssetKeyInternal.EquityIndex(e) -> EquityIndex(e) 

鑑於AssetKey類型的值,你現在可以寫k.Key得到緩存字符串表示,並且可以使用有源圖案上它的模式匹配:

match k with 
| Equity k -> ... 
| EquityIndex i -> ... 
1

你可以考慮做這樣

type CoordRepr = 
| Spot of AssetKey 
| Vol of AssetKey * DateTime option 
| Rate of Currency    

let sortKey = function 
| Spot(key) -> 1,key.ToString() 
| Vol(key,None) -> 2,key.ToString() 
| Vol(key,Some(v)) -> 2,key.ToString() + v.ToShortDateString() 
| Rate(key) -> 3,key.ToString() 

type Coord(repr) = 
    let sortKey = sortKey repr 
    member __.Repr = repr 
    member __.SortKey = sortKey 
    override __.Equals(that) = 
     match that with 
     | :? Coord as c -> sortKey = c.SortKey 
     | _ -> false 
    override __.GetHashCode() = sortKey.GetHashCode() 
    interface System.IComparable with 
     member __.CompareTo(that) = 
      match that with 
      | :? Coord as c -> compare sortKey c.SortKey 
      | _ -> failwith "invalidArg" 

let Spot k = Coord(Spot k) 
let Vol(k,v) = Coord(Vol(k,v)) 
let Rate(k) = Coord(Rate(k)) 

let (|Spot|Vol|Rate|) (c:Coord) = 
    match c.Repr with 
    | Spot k -> Spot k 
    | Vol(k,v) -> Vol(k,v) 
    | Rate k -> Rate k 

的東西,然後使用簽名文件來隱藏CoordReprCoord的CON結構體,sortKey

+0

嗨感謝您的幫助。我實現了第一個版本,速度大約快100倍。這是我的實現: – Daniel 2012-03-01 17:15:50