【问题标题】:F# Active Pattern List.filter or equivalentF# Active Pattern List.filter 或等效的
【发布时间】:2010-04-19 23:04:22
【问题描述】:

我有类型的记录

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

想法?

【问题讨论】:

    标签: list f# active-pattern


    【解决方案1】:

    为了回答你关于活动模式的问题,让我用一个更简单的例子:

    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
    

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

    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)
    

    【讨论】:

    • +1:酷!我不知道您可以将(|Odd|_|) 作为值传递给函数:)
    • 我也很惊讶,当我发现这是可能的。实际上,活动模式有点像运算符。使用运算符,您可以声明let ( ++ ) a b = a + b 并使用它们List.map ( ++ )。使用活动模式:let ( |Xyz|_| ) a = NoneList.map ( |Xyz|_| )(实际上也允许名称中的空格!)
    • 谢谢。我最初将它编写为标准函数(尽管没有那么雄辩),并开始使用主动模式来解锁它们的力量。那里有很好的信息 - 再次感谢您
    【解决方案2】:

    除了 Tomas 关于活动模式的实际细节的观点外,请注意,您始终可以将 fun x -> match x with |... 缩短为 function | ...,这将节省一些击键次数以及弥补可能无意义的标识符的需要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-25
      • 2014-02-28
      • 2013-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多