F#活动模式List.filter或等效

aka*_*nom 4 f# list active-pattern

我有类型的记录

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  ;
}
Run Code Online (Sandbox Code Playgroud)

贸易腿显然是一种交易类型.一条腿可能已经结算或未结算(或未结算但已确认价格) - 因此我已定义了活动模式:

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

然后确定交易是否已经结算(基于匹配LegIsSettled模式的所有支柱:

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

我可以看到使用活动模式的一些优点,但我认为有一种更有效的方法来查看列表中的任何项是否匹配(或不匹配)actie模式而无需专门为其编写lambda表达式它,并使用List.exist.

问题有两个:

  1. 是否有更简洁的方式来表达这一点?
  2. 有没有办法抽象功能/表达

    (fun l -> 
          match l with 
          | LegIsSettled -> false 
          | _ -> true)
    
    Run Code Online (Sandbox Code Playgroud)

这样

let itemMatchesPattern pattern item  =
    match item with
         | pattern -> true
         | _ -> false
Run Code Online (Sandbox Code Playgroud)

我可以写(因为我正在重用这个设计模式):

let curriedItemMatchesPattern = itemMatchesPattern LegIsSettled
if List.exists curriedItemMatchesPattern t.tradeLegs then TradeIsSettled
        else TradeIsUnsettled
Run Code Online (Sandbox Code Playgroud)

思考?

Tom*_*cek 7

要回答有关活动模式的问题,让我使用一个更简单的示例:

let (|Odd|Even|) n = 
  if n % 2 = 0 then Even else Odd
Run Code Online (Sandbox Code Playgroud)

当您声明使用多个选项的模式时,(|Odd|Even|)编译器会将其理解为返回值类型的函数Choice<unit, unit>.因此,您可以使用的活动模式是整个组合|Odd|Even|,而不仅仅是您可以独立使用的两个结构(例如|Odd||Even|).

可以将活动模式视为第一类函数,但如果您使用具有多个选项的模式,则无法对其执行任何操作:

let pattern =(| Odd | Even |);; val模式:int - >选择

您可以编写测试值是否与指定模式匹配的函数,但是您需要很多函数(因为Choice类型参数的数量有很多类型重载):

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

> is1Of2 (|Odd|Even|) 1  
val it : true
Run Code Online (Sandbox Code Playgroud)

像你这样的东西会适用于你的情况,但它远非完美.

如果你声明多个部分活动模式,你可以做得更好一些(但是你当然会松开完整活动模式的一些好方面,比如完整性检查):

let (|Odd|_|) n = 
  if n % 2 = 0 then None else Some()  
let (|Even|_|) n = 
  if n % 2 = 0 then Some() else None
Run Code Online (Sandbox Code Playgroud)

现在您可以编写一个函数来检查值是否与模式匹配:

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
Run Code Online (Sandbox Code Playgroud)

总结虽然可能有一些或多或少的优雅方式来实现您的需求,但我可能会考虑主动模式是否比使用标准函数更具优势.最好先使用函数实现代码,然后决定哪些构造作为活动模式有用,以后再添加活动模式.在这种情况下,通常的代码看起来不会更糟糕:

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)
Run Code Online (Sandbox Code Playgroud)