按任意顺序匹配列表项目

p.s*_*w.g 4 f# list pattern-matching active-pattern

当涉及到F#的许多领域时,我仍然是新手.我出于好奇而不是出于实际的业务需求而提出这个问题.有没有办法匹配列表中的前n个项目,无论它们出现的顺序如何?为了澄清,请考虑以下示例:

type MyEnum = 
    | Foo of int
    | Bar of float
    | Baz of string
let list = [ Foo 1 ; Bar 1.0 ; Baz "1" ]
Run Code Online (Sandbox Code Playgroud)

现在,假设我想调用some_func列表中的前两个项是a Foo和a Bar,以任何顺序.只匹配两种可能的排列很容易:

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"
Run Code Online (Sandbox Code Playgroud)

但是,如果我需要调用一个函数,如果前3项是a Foo,Bar并且Baz按任何顺序怎么办?使用上述相同的技术,要求我写的所有6个不同的排列(或N!用于ñ项目).理想情况下,我希望能够做到这一点:

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"
Run Code Online (Sandbox Code Playgroud)

有没有办法用某种活动模式实现这一点,而不必硬编码不同的排列?

Mar*_*ann 5

这是解决问题的一次尝试.它使用位图对每个联合案例进行评分,并检查得分总和为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
Run Code Online (Sandbox Code Playgroud)

如果得分为7,则截断输入列表并对其进行排序,然后对截断的评分列表使用模式匹配,以创建匹配元素的元组.

这是使用它的一种方法:

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

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"]
Run Code Online (Sandbox Code Playgroud)

AnyOrder 是具有签名的部分活动模式

seq<MyEnum> -> (int * float * string) option
Run Code Online (Sandbox Code Playgroud)