如果你只是想过滤一个列表,那么最简单的选择是使用function来编写标准模式匹配:
[ Foo; Bar; Foo ]
|> List.filter (function Foo -> true | _ -> false)
如果您想编写一些更复杂的通用函数来检查一个案例然后执行其他操作,那么最简单的选择(通常可以使用)是采用返回 true 或 false 的谓词:
let is cond item =
if cond item then
true
else
false
// You can create a predicate using `function` syntax
is (function Foo -> true | _ -> false) <argument>
在您的具体示例中,您有一个有区别的联合,其中没有任何案例有任何参数。这可能是一种不切实际的简化,但是如果您只关心没有参数的可区分联合,那么您可以将这些情况作为值并进行比较:
let is case item =
if case = item then
true
else
false
// You can just pass it 'Foo' as the first parameter to
// `is` and use partial function application
[ Foo; Bar; Foo ]
|> List.filter (is Foo)
// In fact, you can use the built-in equality test operator
[ Foo; Bar; Foo ] |> List.filter ((=) Foo)
如果您有更复杂的可区分联合,其中某些情况具有参数,则最后一种方法将不起作用,因此它可能不是很有用。例如,如果您有一个选项值列表:
let opts = [ Some(42); None; Some(32) ]
opts |> List.filter (is Some) // ERROR - because here you give 'is' a constructor
// 'Some' instead of a value that can be compared.
您可以使用 Reflection 执行各种技巧(检查具有指定名称的案例),还可以使用 F# 引号来获得更好、更安全的语法,但我认为这不值得,因为使用模式匹配使用function 给你非常清晰的代码。
编辑 - 只是出于好奇,一个使用反射的解决方案(而且速度很慢,不是类型安全的,除非你真的知道什么,否则没有人应该在实践中实际使用它你正在做的)可能看起来像这样:
open Microsoft.FSharp.Reflection
open Microsoft.FSharp.Quotations
let is (q:Expr) value =
match q with
| Patterns.Lambda(_, Patterns.NewUnionCase(case, _))
| Patterns.NewUnionCase(case, _) ->
let actualCase, _ = FSharpValue.GetUnionFields(value, value.GetType())
actualCase = case
| _ -> failwith "Wrong argument"
它使用引号来识别联合大小写,所以你可以这样写:
type Case = Foo of int | Bar of string | Zoo
[ Foo 42; Zoo; Bar "hi"; Foo 32; Zoo ]
|> List.filter (is <@ Foo @>)