2010-04-19 48 views
4

我的類型F#活躍模式List.filter或同等

type tradeLeg = { 
    id : int ; 
    tradeId : int ; 
    legActivity : LegActivityType ; 
    actedOn : DateTime ; 
    estimates : legComponents ; 
    entryType : ShareOrDollarBased ; 
    confirmedPrice: DollarsPerShare option; 
    actuals : legComponents option ; 


type trade = { 
    id : int ; 
    securityId : int ; 
    ricCode : string ; 
    tradeActivity : TradeType ; 
    enteredOn : DateTime ; 
    closedOn : DateTime ; 
    tradeLegs : tradeLeg list ; 
} 

一個記錄顯然tradeLegs是一種關貿易。支腿可以解決或懸空(或不安但價格確認) - 因此,我已經定義了有源圖案:

let (|LegIsSettled|LegIsConfirmed|LegIsUnsettled|) (l: tradeLeg) = 
     if Helper.exists l.actuals then LegIsSettled 
     elif Helper.exists l.confirmedPrice then LegIsConfirmed 
     else LegIsUnsettled 

,然後,以確定是否一個貿易結算(基於所有腿匹配LegIsSettled圖案:

let (|TradeIsSettled|TradeIsUnsettled|) (t: trade) = 
     if List.exists (
      fun l -> 
       match l with 
        | LegIsSettled -> false 
        | _ -> true) t.tradeLegs then TradeIsSettled 
     else TradeIsUnsettled 

我可以看到這種使用活動模式的一些優點,但我認爲有一種更有效的方法來查看列表中的任何項目是否匹配(或不)沒有寫入的行爲模式一個專門用於它的lambda表達式,並使用List.exist。

問題有兩方面:

  1. 有沒有更簡潔的表達方式呢?
  2. 是有辦法抽象的功能/表達

    (fun l -> 
         match l with 
         | LegIsSettled -> false 
         | _ -> true) 
    

使得

let itemMatchesPattern pattern item = 
    match item with 
     | pattern -> true 
     | _ -> false 

這樣可以寫(如我重用這個設計模式):

let curriedItemMatchesPattern = itemMatchesPattern LegIsSettled 
if List.exists curriedItemMatchesPattern t.tradeLegs then TradeIsSettled 
     else TradeIsUnsettled 

想法?

回答

6

要解答有關活動模式的問題,讓我用一個簡單的例子:

let (|Odd|Even|) n = 
    if n % 2 = 0 then Even else Odd 

當你聲明,使用(|Odd|Even|),那麼編譯器將其理解爲一個返回值的函數有多個選項的模式類型爲Choice<unit, unit>。因此,您可以使用的活動模式是整個組合|Odd|Even|,而不僅僅是您可以獨立使用的兩個構造(如|Odd||Even|)。

是可能的治療活動性圖案作爲第一類函數,但如果你使用多種選擇模式,你不能做它:

讓利模式=(|奇|偶|) ;; VAL模式:INT - >選擇

您可以編寫函數測試一個值是否符合指定格式,但你需要很多的功能(因爲有很多Choice通過類型數量超載參數):

let is1Of2 pattern item = 
    match pattern item with 
    | Choice1Of2 _ -> true 
    | _ -> false 

> is1Of2 (|Odd|Even|) 1 
val it : true 

類似這樣的東西會適用於你的情況,但它遠非完美。

如果您聲明多個局部活動模式(但你當然鬆動的全面活躍的模式,如完整性檢查一些不錯的方面),你可以做一個好一點的工作:

let (|Odd|_|) n = 
    if n % 2 = 0 then None else Some() 
let (|Even|_|) n = 
    if n % 2 = 0 then Some() else None 

現在你可以寫一個該檢查的值是否匹配模式功能:

let matches pattern value = 
    match pattern value with 
    | Some _ -> true 
    | None -> false 

> matches (|Odd|_|) 1;; 
val it : bool = true 
> matches (|Even|_|) 2;; 
val it : bool = true 

摘要雖然可能有一些或多或少優雅的方式來實現你需要什麼,我可能會考慮主動模式是否連鎖行業e使用標準功能有什麼大的優勢。最好先使用函數實現代碼,然後再決定哪些結構可用作活動模式,並在稍後添加活動模式。在這種情況下,通常的代碼看起來應該不會差多少:

type LegResult = LegIsSettled | LegIsConfirmed | LegIsUnsettled 

let getLegStatus (l: tradeLeg) =  
    if Helper.exists l.actuals then LegIsSettled 
    elif Helper.exists l.confirmedPrice then LegIsConfirmed 
    else LegIsUnsettled 

// Later in the code you would use pattern matching 
match getLegStatus trade with 
| LegIsSettled -> // ... 
| LegIsUnSettled -> // ... 

// But you can still use higher-order functions too 
trades |> List.exist (fun t -> getLegStatus t = LegIsSettled) 

// Which can be rewritten (if you like point-free style): 
trades |> List.exist (getLegStatus >> ((=) LegIsSettled)) 

// Or you can write helper function (which is more readable): 
let legStatusIs check trade = getLegStatus trade = check 
trades |> List.exist (legStatusIs LegIsSettled) 
+0

+1:coolness!我不知道你可以將'(| Odd | _ |)'作爲一個值傳遞給函數:) – Juliet 2010-04-19 23:40:05

+0

我很驚訝,當我發現這是可能的。實際上,活動模式有點像運營商。通過運算符,可以聲明'let(++)a b = a + b'並使用它們'List.map(++)'。使用活動模式:'let(| Xyz | _ |)a = None'和'List.map(| Xyz | _ |)'(名稱中的空格實際上也是允許的!) – 2010-04-19 23:45:14

+0

謝謝。我原本是將它寫成標準功能(儘管不那麼雄辯),並且開始用主動模式來玩弄*解鎖他們的力量。好的信息在那裏 - 再次感謝你 – akaphenom 2010-04-20 00:07:49

4

除了托馬斯的主動模式的實際細節點,請注意,你總是可以縮短fun x -> match x with |...function | ...,這將節省幾個按鍵以及構成潛在無意義標識符的需要。