F#:如何优雅地选择和区分受歧视的工会?

Emi*_*ile 16 f#

说我有一个形状列表:

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)

  • 或者,活动模式. (7认同)

kvb*_*kvb 8

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)


des*_*sco 8

您可以将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)