F# 匹配受歧视的联合案例而不是内容

F# 匹配受歧视的联合案例而不是内容,f#,pattern-matching,discriminated-union,F#,Pattern Matching,Discriminated Union,在F#中,是否有可能根据案例而不是案例内容匹配受歧视的工会?例如,如果我想按大小写为标志的元素筛选列表,是否可以这样筛选?目前,我被迫有三个独立的功能来过滤我想要的方式。这是我迄今为止的做法: type Option = {Id : string Arg : string} type Argument = | Flag of string | Option of Option | Unannotated of string //This is what

在F#中,是否有可能根据案例而不是案例内容匹配受歧视的工会?例如,如果我想按大小写为
标志的元素筛选列表,是否可以这样筛选?目前,我被迫有三个独立的功能来过滤我想要的方式。这是我迄今为止的做法:

type Option = 
 {Id : string
  Arg : string}

type Argument =
     | Flag of string
     | Option of Option
     | Unannotated of string

//This is what I'm going for, but it does not work as the "other" match case will never be matched
let LocateByCase (case:Argument) (args : Argument List) =
    args
    |> List.filter (fun x -> match x with
                             | case -> true
                             | _ -> false)

let LocateAllFlags args =
    args
    |> List.filter (fun x -> match x with
                             | Flag y -> true
                             | _ -> false)
let LocateAllOptions args =
    args
    |> List.filter (fun x -> match x with
                             | Option y -> true
                             | _ -> false)

let LocateAllUnannotated args =
    args
    |> List.filter (fun x -> match x with
                             | Unannotated y -> true
                             | _ -> false)

我是否遗漏了F#语言的某些方面,这些方面会使这更容易处理?

没有内置的方法来找出DU值的情况。面对此类要求时,通常的方法是为每种情况提供适当的功能:

type Argument =
     | Flag of string
     | Option of Option
     | Unannotated of string
    with
     static member isFlag = function Flag _ -> true | _ -> false
     static member isOption = function Option _ -> true | _ -> false
     static member isUnannotated = function Unannotated _ -> true | _ -> false

let LocateByCase case args = List.filter case args

let LocateAllFlags args = LocateByCase Argument.isFlag args
(不用说,
LocateByCase
函数实际上是多余的,但我决定保留它以使答案更清楚)


警告:下面是肮脏的黑客

或者,您可以将案例作为引用提供,并使自己成为一个函数,该函数将分析该引用,从中提取案例名称,并将其与给定值进行比较:

open FSharp.Quotations

let isCase (case: Expr<'t -> Argument>) (value: Argument) = 
    match case with
    | Patterns.Lambda (_, Patterns.NewUnionCase(case, _)) -> case.Name = value.GetType().Name
    | _ -> false

// Usage:
isCase <@ Flag @> (Unannotated "")  // returns false
isCase <@ Flag @> (Flag "")  // returns true
打开FSharp.quotes
让isCase(case:Expr(标志“xyz”)//返回true!
isCase let x=“abc”in Flag x@>(Flag“xyz”)//返回false.WTF?
//等等。。。
如果未来版本的编译器决定以稍有不同的方式生成引用,并且您的代码无法识别它们并始终报告错误否定,则可能会发生另一个问题


如果可能的话,我建议尽量避免混淆引文。表面上看起来很容易,但实际上是这样的。

有趣的是,这实际上可以从C#轻松完成,但F#隐藏了使之容易的方法。尽管如此,你倾向于使用
Flag
,而不是
Flag y
从现在开始,我将在我的代码中这样做。我仍在学习:)有趣的东西,谢谢!您能解释一下带静态成员的
位是什么吗?这就像C#中的对象方法吗?是的,它只是在您的类型上声明一个静态方法。因为您似乎是初学者,这里有一个建议:F#是一种非常好的语言。如果您发现自己试图绕过F#,那么极有可能您的数据建模错误。
let LocateByCase case args = List.filter (isCase case) args

let LocateAllFlags args = LocateByCase <@ Flag @> args
isCase <@ fun() -> Flag "abc" @> (Flag "xyz")  // Returns true!
isCase <@ fun() -> let x = "abc" in Flag x @> (Flag "xyz")  // Returns false. WTF?
// And so on...