Match list items in any order

I'm still new when it comes to many areas of F #. I ask this question more out of curiosity than out of real business need. Is there a way to match the first n items in a list, no matter what order they appear? To clarify, consider the following example:

type MyEnum = 
    | Foo of int
    | Bar of float
    | Baz of string
let list = [ Foo 1 ; Bar 1.0 ; Baz "1" ]

Now suppose I want to name some_funcif the first two items in the list are Fooand a Barin any order. It is easy enough to combine two possible permutations:

let result = 
    match list with
    | Foo n :: Bar x :: _ 
    | Bar x :: Foo n :: _ -> some_func n x
    | _ -> failwith "First 2 items must be Foo and Bar"

However, what if I need to call a function if the first 3 elements are Foo, Barand Bazin any order? Using the same method above, I need to write all 6 different permutations (or n! For n elements). Ideally, I would like to do something in accordance with this:

let result = 
    match list with
    | (AnyOrder [ Foo n ; Bar x ; Baz s ]) :: _ -> some_func n x s
    | _ -> failwith "First 3 items must be Foo, Bar, and Baz"

Is there a way to do this with the active template without requiring hard code of different permutations?

+4
source share
2 answers

A very interesting case. The solution proposed by Mark works fine, but the disadvantage is that the function cannot be reused, I mean the very specific DU for this.

, , DU.

let (|AnyOrderOfFirst|) n = Seq.take n >> Seq.sort >> Seq.toList

let result = 
    match list with
    | AnyOrderOfFirst 3 [ Foo n ; Bar x ; Baz s ] -> some_func n x s
    | _ -> failwith "First 3 items must be Foo, Bar, and Baz"

DU, TAG, , .

, : Unit Test, , .

+4

. , 7:

let (|AnyOrder|_|) s =
    let score = function | Foo _ -> 1 | Bar _ -> 2 | Baz _ -> 4
    let firstElementsWithScores =
        s
        |> Seq.truncate 3
        |> Seq.map (fun x -> x, score x)
        |> Seq.sortBy (fun (_, x) -> x)
        |> Seq.toList
    let sumOfScores =
        firstElementsWithScores |> List.sumBy (fun (_, x) -> x)
    if sumOfScores = 7
    then
        match firstElementsWithScores |> List.map fst with
        | [Foo x ; Bar y ; Baz z ] -> Some (x, y, z)
        | _ -> None
    else None

7, , , , .

:

let checkMatches s =
    match s with
    | AnyOrder (x, y, z) -> [Foo x; Bar y; Baz z]
    | _ -> []

FSI:

> checkMatches list;;
val it : MyEnum list = [Foo 1; Bar 1.0; Baz "1"]
> checkMatches [Foo 1; Bar 1.0; Foo 2];;
val it : MyEnum list = []
> checkMatches [Foo 1; Bar 1.0; Baz "1"; Foo 2];;
val it : MyEnum list = [Foo 1; Bar 1.0; Baz "1"]
> checkMatches [Foo 1; Bar 1.0; Bar 2.0; Foo 2];;
val it : MyEnum list = []
> checkMatches [Bar 1.0; Foo 1; Baz "2.0"; Foo 2];;
val it : MyEnum list = [Foo 1; Bar 1.0; Baz "2.0"]

AnyOrder -

seq<MyEnum> -> (int * float * string) option
+5

All Articles