Joh*_*lph 7 f# pattern-matching discriminated-union
如果还有另一种方法可以实现我在下面尝试做的事情,请告诉我.假设我有以下示例代码
type FooBar =
| Foo
| Bar
let foobars = [Bar;Foo;Bar]
let isFoo item =
match item with
| Foo _ -> true
| _ -> false
foobars |> Seq.filter isFoo
Run Code Online (Sandbox Code Playgroud)
我想编写isFoo的通用/高阶版本,它允许我根据所有其他类型的区分联合(在这种情况下为Bar)过滤我的列表.
像下面的东西,'可以是Foo或Bar
let is<'a> item =
match item with
| a _ -> true
| _ -> false
Run Code Online (Sandbox Code Playgroud)
但是,此尝试会产生以下错误:
错误FS0039:未定义模式鉴别器"a"
如果您只想过滤列表,那么最简单的选择是使用function编写标准模式匹配:
[ Foo; Bar; Foo ]
|> List.filter (function Foo -> true | _ -> false)
Run Code Online (Sandbox Code Playgroud)
如果你想写一个检查的情况下,一些更复杂的通用功能,然后做其他事情,那么最简单的方法(将在一般的工作)是采取一个返回谓词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>
Run Code Online (Sandbox Code Playgroud)
在您的具体示例中,您有一个有区别的联合,其中没有任何案例具有任何参数.这可能是一种不切实际的简化,但是如果你只关心没有参数的有区别的联合,那么你可以将这些案例用作值并进行比较:
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)
Run Code Online (Sandbox Code Playgroud)
如果你有一些更复杂的区分联合,有些情况下有一些参数,那么最后一种方法将不起作用,所以它可能不是很有用.例如,如果您有一个选项值列表:
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.
Run Code Online (Sandbox Code Playgroud)
你可以使用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"
Run Code Online (Sandbox Code Playgroud)
它使用引用来标识union情况,因此您可以编写如下内容:
type Case = Foo of int | Bar of string | Zoo
[ Foo 42; Zoo; Bar "hi"; Foo 32; Zoo ]
|> List.filter (is <@ Foo @>)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2539 次 |
| 最近记录: |