Можно ли исключить словарь операций из этого обобщенного определения множества? - PullRequest
0 голосов
/ 04 мая 2018

Я пытаюсь обобщить концепцию Set в F#. Среди прочего я хочу определить множества, используя неравенства. Это помогло бы мне упростить некоторые разделы моего кода. Поэтому я создал тип MySet следующим образом:

type Comparison = | GE
                  | GT
                  | LE
                  | LT
                  | EQ

type ComparisonOps<'t> = { gt: 't->'t->bool
                           ge: 't->'t->bool
                           eq: 't->'t->bool
                           le: 't->'t->bool
                           lt: 't->'t->bool }

type MySet<'t when 't : comparison> =
    | List of list<'t>
    | Sequence of seq<'t>
    | Array of 't []
    | String of string
    | Set of Set<'t>
    | Compare of (ComparisonOps<'t>*Comparison*'t)

Примечание: я намерен позже сделать MySet рекурсивным, учитывая объединения и пересечения, но для целей этого вопроса это не обязательно.

Весь смысл нового типа MySet состоит в том, чтобы позволить проверять, принадлежат ли элементы разных типов множествам разных случаев. Это реализуется этой функцией:

let elementOf<'t when 't : comparison> (st: MySet<'t>) (x: 't) : bool =
    match st with
    | List xs    -> List.contains x xs   
    | Sequence s -> Seq.contains x s
    | Array a    -> Array.contains x a
    | Set st     -> Set.contains x st
    | String str -> match box str with
                    | :? string as s -> match box x with
                                        | :? string as z -> s.Contains z
                                        | _ -> false
                    | _ -> false
    | Compare (comp: ComparisonOps<'t>*Comparison*'t) ->
        let compOps, cmp, y = comp
        match cmp with
        | GT -> compOps.gt x y
        | GE -> compOps.ge x y
        | EQ -> compOps.eq x y
        | LE -> compOps.le x y
        | LT -> compOps.lt x y

Примечание: я также планирую обобщить elementOf с учетом применения функции, но опять же здесь это не нужно.

Функция работает:

let myStringSet = MySet.String("XYZ")
let strb = "X" |> elementOf<string> myStringSet
printfn "strb = %b" strb // strb = true

let myListSet = MySet.List([0..10])
let listb = 5 |> elementOf<int> myListSet
printfn "listb = %b" listb // listb = true

let myCompSet = MySet.Compare((ComparisonFloat, GT, 0.0))
let compb = -1.0 |> elementOf<float> myCompSet
printfn "compb = %b" compb // compb = false

let myCompSet2 = MySet.Compare((ComparisonString, LT, "XYZ"))
let compb2 = "XA" |> elementOf<string> myCompSet2
printfn "compb2 = %b" compb2 // compb2 = true

Это замечательно, но мне интересно, действительно ли мне нужно создавать словарь операций ComparisonOps, поскольку такие операции, как <, в любом случае полиморфны для типов int, float и string.

Исключение ComparisonOps может значительно упростить код. Это возможно?

1 Ответ

0 голосов
/ 04 мая 2018

Как отмечает Федор Сойкин , звучит так, что, возможно, вам нужно определить набор как все элементы, удовлетворяющие предикату:

type MySet<'t> = | MySet of ('t -> bool)

Тогда операции задания легко определить:

let intersect (MySet p1) (MySet p2) = MySet(fun t -> p1 t && p2 t)

И все ваши конкретные конструкторы можно просто превратить в простые функции:

let ofList l = MySet(fun t -> List.contains t l)
let lt x = MySet(fun t -> t < x)
...