F# Active Pattern 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

Я вижу некоторые преимущества такого использования активных шаблонов, однако я думаю, что есть более эффективный способ увидеть, соответствует ли какой-либо элемент списка шаблону actie (или нет) без необходимости писать лямбда-выражение специально для это, и с помощью 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

Мысли?

2 ответа

Решение

Чтобы ответить на ваш вопрос об активных шаблонах, позвольте мне привести более простой пример:

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

Когда вы объявляете шаблон, который имеет несколько вариантов, используя (|Odd|Even|)то компилятор понимает его как функцию, которая возвращает значение типа Choice<unit, unit>, Таким образом, активным шаблоном, с которым вы можете работать, является целая комбинация |Odd|Even| а не только две конструкции, которые вы могли бы использовать независимо (например, |Odd| а также |Even|).

Можно рассматривать активные шаблоны как функции первого класса, но если вы используете шаблоны с несколькими вариантами, вы не сможете сделать с ними много:

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

Вы можете написать функцию, которая проверяет, соответствует ли значение указанному шаблону, но вам нужно много функций (потому что есть много 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)

В дополнение к пунктам Томаса о фактических деталях активных образцов, обратите внимание, что вы всегда можете сократить fun x -> match x with |... в function | ..., что позволит сэкономить несколько нажатий клавиш, а также необходимость создания потенциально бессмысленного идентификатора.

Другие вопросы по тегам