假设我已经定义了一个用于处理三维向量和度量单位的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返回一个浮点数。
我该如何解决这个问题?如果可能的话,我想在测量单位之间进行转换,而不是使用强制转换,这样编译器可以保证在将角度转换为速度时我做的是正确的。
有什么建议吗?
答案 0 :(得分:4)
这里有几个问题:
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
是内置的)
答案 1 :(得分:1)
如果可能,我想在度量之间执行转换 单元,而不是演员,以编译器可以这样的方式 保证我在转换角度时做的正确 进入速度。
你要求相互矛盾的事情。计量单位通过使用类型推断来强制执行程序中的单元,有助于保证程序的正确性,因此必须明确地进行转换。
正如@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
的类型声明推断。
答案 2 :(得分:1)
好的,这是另一个展示,展示了如何结合标量速度和二维方向来真正利用你的单位:
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
显然,你还没有真正“使用”你的弧度,但这有点预期,无论如何,弧度都是'非'单位。 可以使用它们的一种方法是,如果您希望能够管理弧度和度。您可以在Vector3上添加另外两个静态成员:
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.
您可以查看this question了解更多详情