F# универсальная функция фильтра списка разграниченных союзов
F# вопрос новичка. У меня есть список дискриминационных союзов, таких как:
type Type1 = { name:string; id: int; }
type Type2 = { x: float; y: float;}
type Type3 = { x: float; naam:string}
type Union1 =
| T1 of Type1
| T2 of Type2
| T3 of Type3
let lst1 = [ T1 {name="nnn"; id=3}; T2 {x=1.1; y=1.3}; T1 {name="naam1"; id=39}; T3{x=0.0; naam="xx"}];
//To filter out items of Type1, i do:
let fltT1 (l:list<Union1>) :list<Type1> =
let rec loop (l:list<Union1>) (acc:list<Type1>) =
match l with
| h::t -> match h with
// this is now specific per type
| T1{name=n;id=i} -> loop t ({name=n;id=i}::acc)
| _ -> loop t acc
| [] -> acc
loop l [] |> List.rev
Как я могу сделать такую функцию универсальной, чтобы указать в вызове требуемый тип вывода (Type1|Type2|Type3)?
1 ответ
Мой подход, вероятно, будет просто использовать List.choose
или же Seq.choose
, Имеет преимущества перед filter/map
потому что вам нужно выполнить сопоставление с образцом только один раз, и это гораздо более кратким, чем fold
,
lst1
|> List.choose
(function
|T1 res -> Some res
|_ > None)
choose
это что-то вроде карты и фильтра в сочетании, он возвращает f(x)
для каждого элемента, где этот результат был Some
и игнорирует все элементы, где это было None
, В этом примере тип возвращаемого значения Type1 list
,
Параметризация функции, основанной на конкретном случае объединения, невозможна, потому что конкретные случаи объединения сами по себе не являются типами, они просто конструкторы для типа объединения. T1
в вашем примере это не тип, а Union1
является. Это означает, что в какой-то момент для его разложения требуется явное сопоставление с образцом. (Обратите внимание, что это не относится ко всем функциональным языкам, случаи объединения Scala моделируются с наследованием, но F# принимает подход, используемый такими, как Haskell, Ocaml и т. Д.).
Как упоминал Федор Сойкин, вы можете написать статический член или функцию для проверки каждого случая, если хотите, например:
static member tryAssumeT1 = function
|T1 t1 -> Some t1
| _ -> None
Затем вы можете использовать этот синтаксис:
lst1 |> List.choose (tryAssumeT1)