Почему эта единица измерения ограничена 1? - PullRequest
2 голосов
/ 11 марта 2011

Мне нужно иметь возможность представлять одну и ту же концепцию несколькими разными единицами в F #.Например, я хочу изобразить «расстояние», используя световые годы, астрономические единицы, километры и метры.Я хотел бы использовать универсальную функцию для выполнения расчетов с этими значениями.Вот как я сгруппировал ly, AU, km и m вместе:

[<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)

Этот код вызывается из C # -независимого кода, и это можно сделать, просто указав new LightYearValue(4.2), который станетUnitValue<ly, distance> в F # и может быть передано функции, ожидающей UnitValue<_, distance>.Таким образом, соответствующие единицы входят в функцию, а соответствующие единицы исчезают.Например, если я передал функцию UnitValue<AU, distance>, я мог бы получить обратно float<AU / s ^ 2> в зависимости от расчета - и это было бы подходящее число для шкалы.

Чувствуя себя довольно счастливым,Я начинаю писать тип орбиты:

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))

, но когда я наводю указатель мыши на PeR, он говорит, что его тип UnitValue<1, distance>.Так что же дает?Почему это не работает?Я могу написать функцию, взяв UnitValue<_, distance>, и она отлично работает!Может ли это быть связано с взаимодействием C # с этим кодом?(тип расширяется классом C #) Есть ли способ заставить эту работу: (

1 Ответ

3 голосов
/ 11 марта 2011

При объявлении типа необходимо явно объявить параметры универсального типа (а также параметры модуля). Следующее объявление правильно выводит типы:

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))

(Кстати: вам не нужно переназначать параметры для локальных let привязок - они будут доступны автоматически, поэтому я удалил строки вроде let ApR = ApR)

...