【问题标题】:Pattern Matching of Units of Measure in F#F# 中度量单位的模式匹配
【发布时间】:2011-02-09 14:43:14
【问题描述】:

这个函数:

let convert (v: float<_>) =
  match v with
  | :? float<m> -> v / 0.1<m>
  | :? float<m/s> -> v / 0.2<m/s>
  | _ -> failwith "unknown"

产生错误

类型 'float' 没有任何适当的子类型,不能用作类型测试或运行时强制的来源。

有什么方法可以对度量单位进行模式匹配?

【问题讨论】:

    标签: f# pattern-matching units-of-measurement


    【解决方案1】:

    请参阅http://msdn.microsoft.com/en-us/library/dd233243.aspx 上的运行时单元部分。

    我同意@kvb,我认为最好的解决方法是传递一个对象。

    我想做什么,使用你的代码结构:

    let convert (v: float<_>) =
      match v with
      | :? float<m> -> v<m>
      | :? float<inches> -> v * 2.54 / 100.0<m>
    

    【讨论】:

      【解决方案2】:

      正如@kvb 详细解释的那样,问题在于计量单位是类型的一部分。这意味着float&lt;m&gt;float&lt;m/s&gt; 的类型不同(不幸的是,此信息不会在运行时存储为值的一部分)。

      所以,您实际上是在尝试编写一个可以处理两种不同类型输入的函数。干净的函数式解决方案是声明一个可区分的联合,它可以保存第一种类型或第二种类型的值:

      type SomeValue = 
        | M of float<m>
        | MPS of float<m/s>
      

      然后就可以用普通的模式匹配写函数了:

      let convert v = 
        match v with 
        | M v -> v / 0.1<m>
        | MPS v -> v / 0.2<m/s>
      

      您需要将值显式包装到可区分的联合值中,但这可能是直接执行此操作的唯一方法(无需对程序结构进行一些较大的更改)。

      对于像intfloat 这样的普通类型,您还可以使用重载成员(在某些 F# 类型中声明),但这不适用于度量单位,因为在 F# 之后签名将相同编译器会擦除单元信息。

      【讨论】:

        【解决方案3】:

        您的方法存在两个问题。首先,当你在你的函数定义中使用下划线时,这和使用一个新类型变量是一样的,所以你的定义相当于如下:

        let convert (v: float<'u>) = //'
          match v with
          | :? float<m> -> v / 0.1<m>
          | :? float<m/s> -> v / 0.2<m/s>
          | _ -> failwith "unknown"
        

        错误消息告诉你的是编译器知道v 的类型是float&lt;'u&gt;,而float&lt;'u&gt; 没有适当的子类型,所以没有必要进行类型测试来确定它是否是@ 987654325@ 或任何其他类型。

        您可以尝试通过首先将v 装箱到一个对象中然后进行类型测试来解决这个问题。例如,如果您有一个 list&lt;'a&gt; 并想查看它是否是 list&lt;int&gt;,这将起作用,因为在运行时跟踪有关泛型对象的完整类型信息,包括泛型类型参数(值得注意的是,这与某些其他运行时,如 Java 的工作)。不幸的是,F# 度量单位在运行时被删除,所以这在这里不起作用 - 系统无法在给定盒装表示的情况下推断正确的度量类型,因为在运行时该值只是一个普通的 float - F# 的度量单位系统实际上与 Java 处理泛型类型的方式非常相似。

        顺便说一句,您尝试做的事情似乎很可疑 - 在度量单位中通用的函数不应该根据度量类型做不同的事情;它们应该是适当的参数。你到底想达到什么目的?它看起来肯定不像是对应于物理现实的操作,而物理现实是 F# 度量类型的基础。

        【讨论】:

        • 您好,感谢您的回答。我将不得不再次重新考虑我的代码。一条评论:为什么在使用 Microsoft.FSharp.Math.SI 模块时,单位为 的值没有简化为 ?
        • @Oldrich - 我不清楚为什么不简化(我对规范的阅读表明它应该是),但 F# 知道这些措施是等效的(例如,你可以写 let (x:float&lt;1/s&gt;) = 1.0&lt;Pa m s / kg&gt;并且编译器将允许它)。出于显示目的,Pa 等缩写词可能并不总是展开。
        猜你喜欢
        • 1970-01-01
        • 2013-10-19
        • 1970-01-01
        • 2015-01-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多