2011-03-11 4 views
2

Je dois être capable de représenter le même concept avec quelques unités différentes en F #. Par exemple, je veux représenter la «distance» en utilisant les années-lumière, les unités astronomiques, les kilomètres et les mètres. Je voudrais utiliser une fonction générique pour faire des calculs avec ces valeurs. Voilà comment j'ai regroupé ly, AU, km, et m ensemble:Pourquoi cette unité de mesure est-elle limitée à 1?

[<Measure>] type ly 
[<Measure>] type AU 
[<Measure>] type km 
[<Measure>] type m 

[<Measure>] type distance 

type UnitValue<[<Measure>] 'u, [<Measure>] 't> = 
    val conversionFactor : float<'t/'u> 
    val value : float<'u> 
    new (v, cf) = { value = FloatWithMeasure<'u> v; conversionFactor = FloatWithMeasure<'t/'u> cf } 
    member this.toUnits = this.value * this.conversionFactor 
    member this.fromUnits (x : float<'t>) = x/this.conversionFactor 
    static member (+) (a : UnitValue<'u, 't>, b : UnitValue<_, 't>) = 
     a.newValue (a.toUnits + b.toUnits) 
    static member (-) (a : UnitValue<'u, 't>, b : UnitValue<_, 't>) = 
     a.newValue (a.toUnits - b.toUnits) 
    static member (*) (a : UnitValue<'u, 't>, b : float) = 
     a.newValue (a.toUnits * b) 
    member this.newValue (x : float<'t>) = 
     new UnitValue<'u, 't>(float (this.fromUnits x), float this.conversionFactor) 

//Distance units 
type LightYearValue(value) = 
    inherit UnitValue<ly, distance>(value, 6324.0) 

type AstronomicalUnitValue(value) = 
    inherit UnitValue<AU, distance>(value, 15.0) 

type KilometerValue(value) = 
    inherit UnitValue<km, distance>(value, 0.00001) 

type MeterValue(value) = 
    inherit UnitValue<m, distance>(value, 0.0000000) 

Ce code est appelé de l'unité ne prenant pas en C# et peut être fait en spécifiant simplement new LightYearValue(4.2), qui deviendra un UnitValue<ly, distance> en F # , et peut être passé à une fonction qui attend un UnitValue<_, distance>. De cette façon, les unités appropriées entrent dans la fonction, et les unités appropriées sortent. Par exemple, si j'ai passé la fonction UnitValue<AU, distance>, je pourrais récupérer un float<AU/s^2> en fonction du calcul - et ce serait un nombre approprié pour l'échelle.

Se sentant très heureux avec cela, je commence à écrire un type Orbit:

and Orbit(PeR : UnitValue<_, distance>, ApR : UnitValue<_, distance>, AgP : float, focus : SphericalMass) = 
    let PeR = PeR 
    let ApR = ApR 
    let AgP = AgP 
    let focus = focus 
    let Maj = PeR + ApR 
    let Ecc = (Maj.value - (2.0 * PeR.value))/Maj.value 
    let DistanceAt theta = 
     (Maj.value/2.0) * (1.0 - Ecc ** 2.0)/(1.0 + Ecc * Math.Cos(theta)) 

mais quand je passe la souris au-dessus de ma souris PeR, il dit son type est UnitValue<1, distance>. Alors qu'est-ce qui donne? Pourquoi ça ne marche pas? Je peux écrire une fonction en prenant UnitValue<_, distance> et ça marche bien! Cela pourrait-il avoir à voir avec C# interagir avec ce code? (le type est étendu par une classe C#) Y a-t-il un moyen de faire fonctionner ceci :(

Répondre

3

Lors de la déclaration d'un type, vous devez déclarer explicitement les paramètres de type générique (ainsi que les paramètres d'unité). types correctement:

type Orbit<[<Measure>] 'u, [<Measure>] 'v> 
    (PeR : UnitValue<'u, distance>, ApR : UnitValue<'v, distance>, 
     AgP : float, focus : SphericalMass) = 
    let Maj = PeR + ApR 
    let Ecc = (Maj.value - (2.0 * PeR.value))/Maj.value 
    let DistanceAt theta = 
     (Maj.value/2.0) * (1.0 - Ecc ** 2.0)/(1.0 + Ecc * Math.Cos(theta)) 

(BTW: Vous n'avez pas besoin de paramètres réattribuer aux let locales des liaisons - je, ils seront accessibles automatiquement, retiré des lignes comme let ApR = ApR)!

+0

bien sûr j'étais genre de peur de cela:/Je ne voulais pas déclarer les unités partout –

Questions connexes