题
假设我已经定义了一个用于处理三维向量和测量单元的F#模块:
[<Measure>]
type m
[<Measure>]
type s
[<Measure>]
type v = m/s
[<Measure>]
type rad
type Vector3<[<Measure>] 'a> =
{
X : float<'a>
Y : float<'a>
Z : float<'a>
}
在我的某个地方,我在弧度中表达了一个角度:
let angle:float32<rad> = 0.5f<rad>
现在,我必须使用角度来计算其组件来声明vector3(速度)。我尝试了类似的东西:
let velocity : Vector3<m/s> = { X = Math.Cos(angle);Y = Math.Sin(angle);Z = 0.0<m/s>} //don't compile
上面的代码未编译,因为Vector3期望在X和Y中具有值,但SIN返回浮动。
我怎么解决这个问题?如果可能的话,我想以量度单位之间的转换,而不是铸件,以使编译器可以保证我在将角度转换为速度的同时做正确的事情。
有建议吗?
解决方案
这里有几个问题:cos
期待无单位的价值,因此您必须脱下单位 angle
;鉴于速度正在期待 float
并不是 float32
, ,您最好只是直接转换为 float
(删除单位)。
然后,您需要重新安装单元。在测量单元的第一个版本中,您可以通过在适当的度量中简单地乘以1来做到这一点。现在有 LanguagePrimitives.FloatWithMeasure
, ,这更正确,但详细。
let velocity =
{
X = angle |> float |> cos |> LanguagePrimitives.FloatWithMeasure<m/s> ;
Y = angle |> float |> sin |> LanguagePrimitives.FloatWithMeasure<m/s> ;
Z = 0.0<m/s> ;
}
除此之外,10个弧度是一个有趣的角度...
(注意 cos
和 sin
内置)
其他提示
好的,这是另一种看法,展示了如何结合标量速度和2D方向以真正利用您的单元:
let vvector (velocity:float<'u>) (xydirection:float<rad>) =
{
X = cos (float xydirection) * velocity;
Y = sin (float xydirection) * velocity;
Z = 0.<_>
}
这使:
> vvector 15.3<m/s> angle<rad>;;
val it : Vector3<m/s> = {X = 13.4270132;
Y = 7.335210741;
Z = 0.0;}
您可以通过将操作员添加到Vector3类型中,将其进一步迈出一步:
static member (*) (v1: Vector3<'u>, n: float<'v>) =
{ X = v1.X * n; Y = v1.Y * n; Z = v1.Z * n }
这意味着您可以这样做:
let vvector2 (velocity:float<'u>) (xydirection:float<rad>) =
{ X = cos(float xydirection); Y = sin(float xydirection); Z = 0. } * velocity
显然,您仍然并不是真正地“使用”那里的弧度,但这是一种预期的是,弧度无论如何都是“非单元”。你的一种方式 可以 使用它们是如果您想能够管理弧度 和 学位。您可以在vector上添加其他两个静态成员3:
static member ofAngle (ang:float<rad>) =
{ X = cos(float ang); Y = sin(float ang); Z = 0.0 }
static member ofAngle (ang:float<degree>) =
Vector3<'u>.ofAngle(ang * 2.<rad> * System.Math.PI / 360.<degree>)
看起来不错,但不幸的是,它不会编译,因为 The method 'ofAngle' has the same name and signature as another method in this type once tuples, functions and/or units of measure are erased.
你可以退房 这个问题 更多细节
如果可能的话,我想以量度单位之间的转换,而不是铸件,以使编译器可以保证我在将角度转换为速度的同时做正确的事情。
您要求矛盾的事情。测量单位有助于通过使用类型推理在程序中执行单位来保证程序的正确性,因此必须明确进行转换。
正如@Benjol所说,您必须在尺寸和无量纲数据之间进行转换。以下代码是一个非正式版本,我从中转换 float
至 float<m/s>
通过乘以其单位值:
let angle:float32<rad> = 0.5f<rad>
let velocity = {
X = sin (float angle) * 1.0<m/s> ;
Y = cos (float angle) * 1.0<_> ;
Z = 0.0<_>
}
请注意,您只需要根据X指定单位,其他单位是根据类型声明来推断的 Vector3
.