【问题标题】:In F#, is it possible to check if two values have the same constructor?在 F# 中,是否可以检查两个值是否具有相同的构造函数?
【发布时间】:2018-02-11 08:40:09
【问题描述】:

假设我有一个大的可区分联合类型:

type Thing =
    | One of int
    | Two of string
    | Three of bool option
      ...

我有这个功能:

let comp a b = match a, b with
    | One _, One _ -> true
    | Two _, Two _ -> true
      ...
    | _, _ -> false

有没有办法以一种更简洁、更短的方式编写函数,而不需要我列出每个构造函数?

【问题讨论】:

  • 你可以使用reflection: let comp a b = a.GetType() = b.GetType()
  • @Funk 我没有意识到反射会如此简洁。这有缺点吗?
  • @TheQuickBrownFox:它利用了 DU 案例在 IL 中如何编译成嵌套类的优势。从实际的角度来看,这并不是一个巨大的缺点,但重要的是要认识到 F# 构造是如何编译到 IL 中的(这并不总是直截了当的),因为这是您在使用反射 API 时工作的层。
  • @TheQuickBrownFox:它也肯定会更慢,但如果在这种特殊情况下这是一个问题,你可以称自己很幸运。
  • GetType() 仅在案例有参数时才有效;没有参数的情况不会编译到嵌套的子类中。

标签: function f# functional-programming pattern-matching


【解决方案1】:

基本上,这是不可能的。即使您可以获得值的构造函数,它们也无法比较,因为它们是函数。这涉及到一些样板文件,但您可以定义标签值和映射到标签的函数:

let thingCase thing =
    match thing with
    | One _ -> 1
    | Two _ -> 2
    | Three _ -> 3

let comp a b = thingCase a = thingCase b

这足够灵活,也可以处理序列:

let compSeq things =
    things
    |> Seq.map thingCase
    |> Seq.pairwise
    |> Seq.forall (fun (a, b) -> a = b)

注意:您也可以通过反射来执行此操作,但通常最好避免。

【讨论】:

    【解决方案2】:

    我不太确定它的性能有多好,但可以使用FSharp.Reflection 来做到这一点。

    open FSharp.Reflection
    
    type Thing =
        | One of int
        | Two of string
        | Three of bool option
    
    let tagOfThing = FSharpValue.PreComputeUnionTagReader(typeof<Thing>)
    // val tagOfThing : obj -> int
    
    let isSameThingCase (a: Thing) (b: Thing) =
        tagOfThing a = tagOfThing b
    

    用途:

    > isSameThingCase (One 1) (One 2);;
    val it : bool = true
    > isSameThingCase (Two "test") (Three None);;
    val it : bool = false
    

    【讨论】:

      猜你喜欢
      • 2014-02-03
      • 2015-08-24
      • 1970-01-01
      • 2013-01-10
      • 1970-01-01
      • 2017-11-02
      • 1970-01-01
      • 2012-09-19
      • 1970-01-01
      相关资源
      最近更新 更多