2010-04-19 51 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 | ...,这将节省几个按键以及构成潜在无意义标识符的需要。