F# Active Pattern List.filter 或等效项

发布于 2024-08-29 10:40:17 字数 1840 浏览 12 评论 0原文

我有一个类型的记录

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

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

活动 有两个方面:

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

    (有趣的 l -> 
          将 l 与 
          |立法已解决 ->错误的 
          | _->真的)
    

,以便

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

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

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

想法?

I have a records of types

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  ;
}

Obviously the tradeLegs are a type off of a trade. A leg may be settled or unsettled (or unsettled but price confirmed) - thus I have defined the active pattern:

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

and then to determine if a trade is settled (based on all legs matching LegIsSettled pattern:

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

I can see some advantages of this use of active patterns, however i would think there is a more efficient way to see if any item of a list either matches (or doesn't) an actie pattern without having to write a lambda expression specifically for it, and using List.exist.

Question is two fold:

  1. is there a more concise way to express this?
  2. is there a way to abstract the functionality / expression

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

Such that

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

such I could write (as I am reusing this design-pattern):

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

Thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绝不放开 2024-09-05 10:40:17

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

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

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

可以将活动模式视为第一类函数,但如果您使用具有多个选项的模式,则不能用它做太多事情:

让模式 = (|奇数|偶数|);;
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)

To answer your question about active patterns, let me use a simpler example:

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

When you declare a pattern that has multiple options using (|Odd|Even|), then the compiler understands it as a function that returns a value of type Choice<unit, unit>. So, the active pattern that you can work with is the whole combination |Odd|Even| and not just two constructs that you could use independently (such as |Odd| and |Even|).

It is possible to treat active patterns as first class functions, but if you're using patterns with multiple options, you cannot do much with it:

let pattern = (|Odd|Even|);;
val pattern : int -> Choice

You can write function that tests whether a value matches a specified pattern, but you'd need a lot of functions (because there are many Choice types overloaded by the number of type parameters):

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

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

Something like this would work in your case, but it is far from being perfect.

You can do a little better job if you declare multiple partial active patterns (but then you of course loose some nice aspects of full active patterns such as completeness checking):

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

Now you can write a function that checks whether a value matches pattern:

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

Summary While there may be some more or less elegant way to achieve what you need, I'd probably consider whether active patterns give you any big advantage over using standard functions. It may be a better idea to implenent the code using functions first and then decide which of the constructs would be useful as active patterns and add active patterns later. In this case, the usual code wouldn't look much worse:

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)
淡看悲欢离合 2024-09-05 10:40:17

除了 Tomas 关于活动模式实际细节的观点之外,请注意,您始终可以缩短 fun x ->;将 x 与 |... 匹配到 function | ...,这将节省一些击键次数,并且无需编写可能无意义的标识符。

In addition to Tomas's points on the actual details of active patterns, note that you can always shorten fun x -> match x with |... to function | ..., which will save a few keystrokes as well as the need to make up a potentially meaningless identifier.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文