说我有一个形状列表:
type shape =
| Circle of float
| Rectangle of float * float
let a = [ Circle 5.0; Rectangle (4.0, 6.0)]
Run Code Online (Sandbox Code Playgroud)
我怎样才能测试一个圆圈是否存在?我可以为每个形状创建一个函数
let isCircle s =
match s with
| Circle -> true
| _ -> false
List.exists isCircle a
Run Code Online (Sandbox Code Playgroud)
但我觉得必须有F#更优雅的方式,不必定义这样的功能对于每个形状类型等.在那儿?
相关问题是如何根据形状类型对形状列表进行分组:
a |> seq.groupBy( <shapetype? >)
Run Code Online (Sandbox Code Playgroud)
RD1*_*RD1 16
如果您对不同类别的形状感兴趣,那么定义另一种完全捕获它们的类型是有意义的:
type shapeCategory = Circular | Rectangular
let categorize = function
| Circle _ -> Circular
| Rectangle _ -> Rectangular
List.exists ((=) Circular) (List.map categorize a)
a |> Seq.groupBy(categorize)
Run Code Online (Sandbox Code Playgroud)
编辑 - 根据Brian的建议,您也可以使用活动模式而不是新类型.对于您的示例,它的效果非常相似,但是会更好地扩展到更复杂的模式,而如果您的代码经常使用类别,并且您希望为它们提供一个好的联合类型而不是Choice类型,则上述方法可能会更好.
let (|Circular|Rectangular|) = function
| Circle _ -> Circular
| Rectangle _ -> Rectangular
List.exists (function Circular -> true | _ -> false) a
let categorize : shape -> Choice<unit, unit> = (|Circular|Rectangular|)
a |> Seq.groupBy(categorize)
Run Code Online (Sandbox Code Playgroud)
You can use the F# reflection library to get a value's tag:
let getTag (a:'a) =
let (uc,_) = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(a, typeof<'a>)
uc.Name
a |> Seq.groupBy getTag
Run Code Online (Sandbox Code Playgroud)
您可以将F#反射与引用结合起来以获得通用解决方案
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
type Shape =
| Circle of float
| Rectangle of float * float
let isUnionCase (c : Expr<_ -> 'T>) =
match c with
| Lambda (_, NewUnionCase(uci, _)) ->
let tagReader = Microsoft.FSharp.Reflection.FSharpValue.PreComputeUnionTagReader(uci.DeclaringType)
fun (v : 'T) -> (tagReader v) = uci.Tag
| _ -> failwith "Invalid expression"
let a =
[ Circle 5.0; Rectangle (4.0, 6.0)]
|> List.filter (isUnionCase <@ Rectangle @>)
printf "%A" a
Run Code Online (Sandbox Code Playgroud)