3

假设我已经定义了一个 F# 模块来处理 3 维向量和测量单位:

[<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 返回一个浮点数。

我怎么解决这个问题?如果可能的话,我想在测量单位之间执行转换,而不是强制转换,这样编译器可以保证我在将角度转换为速度的同时做正确的事情。

有什么建议吗?

4

3 回答 3

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 弧度是一个有趣的角度......

(注意cossin是内置的)

于 2011-11-29T09:41:20.657 回答
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

于 2011-11-29T10:05:40.433 回答
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.您可以查看这个问题以获取更多详细信息

于 2011-11-29T11:04:05.217 回答