匹配受歧视的工会案例而不是内容
Match on discriminated union case not contents
在 F# 中是否可以根据大小写而不是大小写内容来匹配可区分联合?例如,如果我想按大小写 Flag
的元素过滤列表,是否可以这样过滤?目前,我被迫拥有三个独立的功能来过滤我想要的方式。这是我目前采用的方法:
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
然后用这个函数过滤:
let LocateByCase case args = List.filter (isCase case) args
let LocateAllFlags args = LocateByCase <@ Flag @> args
然而,这本质上是一个肮脏的黑客。它的肮脏和 hackiness 来自这样一个事实,因为你不能在编译时要求特定的引号形状,它会允许无意义的程序。例如:
isCase <@ fun() -> Flag "abc" @> (Flag "xyz") // Returns true!
isCase <@ fun() -> let x = "abc" in Flag x @> (Flag "xyz") // Returns false. WTF?
// And so on...
如果编译器的未来版本决定以稍微不同的方式生成引号,则可能会出现另一个陷阱,并且您的代码将无法识别它们并始终报告漏报。
我建议尽可能避免混淆引号。表面上看起来很容易,但实际上是 easy over simple.
在 F# 中是否可以根据大小写而不是大小写内容来匹配可区分联合?例如,如果我想按大小写 Flag
的元素过滤列表,是否可以这样过滤?目前,我被迫拥有三个独立的功能来过滤我想要的方式。这是我目前采用的方法:
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
然后用这个函数过滤:
let LocateByCase case args = List.filter (isCase case) args
let LocateAllFlags args = LocateByCase <@ Flag @> args
然而,这本质上是一个肮脏的黑客。它的肮脏和 hackiness 来自这样一个事实,因为你不能在编译时要求特定的引号形状,它会允许无意义的程序。例如:
isCase <@ fun() -> Flag "abc" @> (Flag "xyz") // Returns true!
isCase <@ fun() -> let x = "abc" in Flag x @> (Flag "xyz") // Returns false. WTF?
// And so on...
如果编译器的未来版本决定以稍微不同的方式生成引号,则可能会出现另一个陷阱,并且您的代码将无法识别它们并始终报告漏报。
我建议尽可能避免混淆引号。表面上看起来很容易,但实际上是 easy over simple.