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)
Другие вопросы по тегам