質問
3次元ベクトルを処理し、測定ユニットを処理するための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
明らかに、あなたはまだそこにあなたのラジアンを「使用」していませんが、それはある種の予想されています、とにかくラジアンは一種の「非」ユニットです。あなたの一つの方法 できる それらを利用することは、あなたがラジアンを管理できるようにしたい場合です と 度。 Vector3に他の2つの静的メンバーを追加できます。
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
.