F#运算符重载,用于转换多个不同的度量单位 [英] F# operator overloading for conversion of multiple different units of measure

查看:95
本文介绍了F#运算符重载,用于转换多个不同的度量单位的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够做到这一点:

I want to be able to do this:

let duration = 1<hours> + 2<minutes> + 3<seconds>

具有以下类型和功能(可能还有更多的计量单位):

with the following types and functions (and possibly more units of measure):

type [<Measure>] seconds     
type [<Measure>] minutes
type [<Measure>] hours

let seconds_per_minute = 60<seconds> / 1<minutes>
let minutes_per_hour = 60<minutes> / 1<hours>

let minutes_to_seconds minutes seconds = minutes * seconds_per_minute + seconds
let hours_to_minutes hours minutes = hours * minutes_per_hour + minutes

因此,基本上,"hours_to_minutes"应该用来添加小时和分钟,而当我像上面那样键入时,"minutes_to_seconds"应该用来添加分钟和秒.

So basically "hours_to_minutes" should be used for adding hours and minutes, and "minutes_to_seconds" should be used for adding minutes and seconds when I type it in like above.

这可以在F#中执行吗?

推荐答案

实际上有可能,有一种方法可以做到这一点:

Actually it is possible, there is a way to do this:

type [<Measure>] seconds     
type [<Measure>] minutes
type [<Measure>] hours

let seconds_per_minute = 60<seconds> / 1<minutes>
let minutes_per_hour = 60<minutes> / 1<hours>

let minutes_to_seconds minutes seconds = minutes * seconds_per_minute + seconds
let hours_to_minutes hours minutes = hours * minutes_per_hour + minutes

type D1 = D1
type D2 = D2

type Sum = Sum with
  static member inline ($) (Sum, _:^t when ^t: null and ^t: struct) = id
  static member inline ($) (Sum, b)              = fun _  _  a -> a + b
  static member        ($) (Sum, b:int<minutes>) = fun D1 _  a -> hours_to_minutes   a b
  static member        ($) (Sum, b:int<seconds>) = fun D1 D2 a -> minutes_to_seconds a b    

let inline (+) a b :'t = (Sum $ b) D1 D2 a

let duration = 1<hours> + 2<minutes> + 3<seconds>

但是它确实很hacky,我不推荐.

But it's really hacky, I wouldn't recommend it.

更新

根据此处的评论提供一些答案:

Based on the comments here are some answers:

  • 此技术使用在编译时解决的重载,因此在运行时不会降低性能.它基于我前一段时间在我的博客中写的内容.

要添加更多的重载,您将不得不添加更多的虚拟参数(D3D4,...),最终,如果您决定添加一些与现有重载冲突的重载,则可能必须使用三元运算符(?<-)或具有显式静态成员约束的函数调用. 此处是示例代码.

To add more overloads you will have to add more dummy parameters (D3, D4, ...) and eventually if you decide to add some overloads which conflicts with an existing one you may have to use a ternary operator (?<-) or a function call with explicit static member constraints. Here's a sample code.

我想我不会使用它,因为它需要许多技巧(虚拟重载和2个虚拟类型)并且代码变得可读性较差.最终,如果F#为基于重载的内联函数添加更多支持,我肯定会考虑的.

I think I wouldn't use it since it requires many hacks (a Dummy overload and 2 dummy types) and the code becomes less readable. Eventually if F# adds more support for inline functions based on overloads I would definitely consider it.

Phil Trelford的技术(在里德的答案中提到)有效在运行时,第三个选择是使用幻像类型,它可能需要更少的hack.

Phil Trelford's technique (mentioned in Reed's answer) works at run-time, a 3rd option would be to use phantom types, it may require less hacks.

结论

如果必须在所有替代方法之间进行选择,我将使用此技术,但要在调用站点更加明确,我的意思是我将定义诸如minutesseconds的转换函数,并且在调用站点上我会这样写:

If I had to choose between all alternatives I would use this technique but being more explicit at the call site, I mean I would define conversion functions like minutes, seconds and that way at the call site I would write:

let duration = seconds 1<hours> + seconds 2<minutes> + 3<seconds>

然后要定义这些转换函数,我将使用重载,但与重新定义现有的二进制运算符相比,它不那么笨拙.

And then to define those conversion functions I would use overloads, but it would be less hacky than re-defining an existing binary operator.

这篇关于F#运算符重载,用于转换多个不同的度量单位的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆