Jednostki miary

Wartości liczb zmiennoprzecinkowych i liczb całkowitych ze znakiem w języku F# mogą mieć skojarzone jednostki miary, które są zwykle używane do wskazywania długości, objętości, masy itd. Korzystając z ilości z jednostkami, można włączyć kompilator w celu sprawdzenia, czy relacje arytmetyczne mają poprawne jednostki, co pomaga zapobiegać błędom programowania.

Uwaga

W tych przykładach pokazano poprawność w obliczeniach arytmetycznych obejmujących jednostki miary. Funkcja może być również używana do dodawania bezpiecznej adnotacji typu z zerowymi kosztami reprezentacji do innych typów przy użyciu podejścia takiego jak projekt FSharp.UMX .

Składnia

[<Measure>] type unit-name [ = measure ]

Uwagi

Poprzednia składnia definiuje nazwę jednostki jako jednostkę miary. Opcjonalna część służy do definiowania nowej miary pod względem wcześniej zdefiniowanych jednostek. Na przykład następujący wiersz definiuje miarę cm (centymetr).

[<Measure>] type cm

Poniższa linia definiuje miarę ml (mililiter) jako centymetr sześcienny (cm^3).

[<Measure>] type ml = cm^3

W poprzedniej składni miara jest formułą obejmującą jednostki. W formułach, które obejmują jednostki, moce całkowite są obsługiwane (dodatnie i ujemne), odstępy między jednostkami wskazują również produkt dwóch jednostek * i / wskazuje iloraz jednostek. W przypadku jednostki wzajemnej można użyć ujemnej mocy całkowitej lub wartości / wskazującej separację między licznikiem a mianownikiem formuły jednostkowej. Wiele jednostek w mianowniku powinno być otoczony nawiasami. Jednostki oddzielone spacjami po / interpretowaniu jako część mianownika, ale wszystkie jednostki po * obiekcie są interpretowane jako część licznika.

W wyrażeniach jednostkowych można użyć wartości 1, aby wskazać ilość bez wymiarów lub razem z innymi jednostkami, takimi jak w liczniku. Na przykład jednostki stawki będą zapisywane jako 1/s, gdzie s wskazuje sekundy. Nawiasy nie są używane w formułach jednostkowych. Nie określasz stałych konwersji liczbowej w formułach jednostkowych; można jednak definiować stałe konwersji z jednostkami oddzielnie i używać ich w obliczeniach sprawdzanych jednostkowo.

Formuły jednostkowe, które oznaczają, że to samo można napisać na różne równoważne sposoby. W związku z tym kompilator konwertuje formuły jednostek na spójną formę, która konwertuje negatywne moce na wzajemne, grupuje jednostki na pojedynczy licznik i mianownik, a także alfabetyzuje jednostki w liczniku i mianowniku.

Na przykład formuły jednostkowe kg m s^-2 i m /s s * kg są konwertowane na kg m/s^2.

Jednostki miary są używane w wyrażeniach zmiennoprzecinkowych. Użycie liczb zmiennoprzecinkowych wraz ze skojarzonymi jednostkami miary dodaje kolejny poziom bezpieczeństwa typu i pomaga uniknąć błędów niezgodności jednostek, które mogą wystąpić w formułach, gdy używasz słabo wpisanych liczb zmiennoprzecinkowych. Jeśli napiszesz wyrażenie zmiennoprzecinkowe używające jednostek, jednostki w wyrażeniu muszą być zgodne.

Można dodawać adnotacje do literałów za pomocą formuły jednostki w nawiasach kątowych, jak pokazano w poniższych przykładach.

1.0<cm>
55.0<miles/hour>

Nie umieszczasz odstępu między liczbą a nawiasem kątowym; można jednak uwzględnić sufiks literału, taki jak f, jak w poniższym przykładzie.

// The f indicates single-precision floating point.
55.0f<miles/hour>

Taka adnotacja zmienia typ literału z jego typu pierwotnego (na floatprzykład ) na typ wymiarowy, taki jak float<cm> lub, w tym przypadku, float<miles/hour>. Adnotacja jednostkowa <1> wskazuje bezwymiarową ilość, a jego typ jest odpowiednikiem typu pierwotnego bez parametru jednostkowego.

Typ jednostki miary to zmiennoprzecinkowa lub podpisany typ całkowity wraz z dodatkową adnotacją jednostkową wskazaną w nawiasach kwadratowych. W związku z tym podczas pisania typu konwersji z g (gramy) na kg (kilogramy) należy opisać typy w następujący sposób.

let convertg2kg (x : float<g>) = x / 1000.0<g/kg>

Jednostki miary są używane do sprawdzania jednostek czasu kompilacji, ale nie są utrwalane w środowisku czasu wykonywania. W związku z tym nie wpływają one na wydajność.

Jednostki miary można stosować do dowolnego typu, a nie tylko typów zmiennoprzecinkowych; jednak tylko typy zmiennoprzecinkowe, typy ze znakiem całkowitym i typy dziesiętne obsługują ilości wymiarowe. W związku z tym warto używać tylko jednostek miary dla typów pierwotnych i agregacji zawierających te typy pierwotne.

Poniższy przykład ilustruje użycie jednostek miary.

// Mass, grams.
[<Measure>] type g
// Mass, kilograms.
[<Measure>] type kg
// Weight, pounds.
[<Measure>] type lb

// Distance, meters.
[<Measure>] type m
// Distance, cm
[<Measure>] type cm

// Distance, inches.
[<Measure>] type inch
// Distance, feet
[<Measure>] type ft

// Time, seconds.
[<Measure>] type s

// Force, Newtons.
[<Measure>] type N = kg m / s^2

// Pressure, bar.
[<Measure>] type bar
// Pressure, Pascals
[<Measure>] type Pa = N / m^2

// Volume, milliliters.
[<Measure>] type ml
// Volume, liters.
[<Measure>] type L

// Define conversion constants.
let gramsPerKilogram : float<g kg^-1> = 1000.0<g/kg>
let cmPerMeter : float<cm/m> = 100.0<cm/m>
let cmPerInch : float<cm/inch> = 2.54<cm/inch>

let mlPerCubicCentimeter : float<ml/cm^3> = 1.0<ml/cm^3>
let mlPerLiter : float<ml/L> = 1000.0<ml/L>

// Define conversion functions.
let convertGramsToKilograms (x : float<g>) = x / gramsPerKilogram
let convertCentimetersToInches (x : float<cm>) = x / cmPerInch

Poniższy przykład kodu ilustruje sposób konwertowania z bezwymiarowej liczby zmiennoprzecinkowej na wartość zmiennoprzecinkową wymiarową. Wystarczy pomnożyć wartość 1.0, stosując wymiary do wersji 1.0. Można to abstrahować do funkcji, takiej jak degreesFahrenheit.

Ponadto po przekazaniu wartości wymiarowych do funkcji, które oczekują liczb zmiennoprzecinkowych bez wymiarów, należy anulować jednostki lub rzutować je float przy użyciu float operatora . W tym przykładzie argumenty printf są dzielone według1.0<degC>, ponieważ printf oczekuje się ilości wymiarowych.

[<Measure>] type degC // temperature, Celsius/Centigrade
[<Measure>] type degF // temperature, Fahrenheit

let convertCtoF ( temp : float<degC> ) = 9.0<degF> / 5.0<degC> * temp + 32.0<degF>
let convertFtoC ( temp: float<degF> ) = 5.0<degC> / 9.0<degF> * ( temp - 32.0<degF>)

// Define conversion functions from dimensionless floating point values.
let degreesFahrenheit temp = temp * 1.0<degF>
let degreesCelsius temp = temp * 1.0<degC>

printfn "Enter a temperature in degrees Fahrenheit."
let input = System.Console.ReadLine()
let parsedOk, floatValue = System.Double.TryParse(input)
if parsedOk
   then
      printfn "That temperature in Celsius is %8.2f degrees C." ((convertFtoC (degreesFahrenheit floatValue))/(1.0<degC>))
   else
      printfn "Error parsing input."

W poniższej przykładowej sesji przedstawiono dane wyjściowe z i dane wejściowe do tego kodu.

Enter a temperature in degrees Fahrenheit.
90
That temperature in degrees Celsius is    32.22.

Typy pierwotne obsługujące jednostki miary

Następujące typy lub aliasy skrótów typów obsługują adnotacje typu unit-of-measure:

Alias języka F# Typ CLR
float32/single System.Single
float/double System.Double
decimal System.Decimal
sbyte/int8 System.SByte
int16 System.Int16
int/int32 System.Int32
int64 System.Int64
byte/uint8 System.Byte
uint16 System.UInt16
uint/uint32 System.UInt32
uint64 System.UIn64
nativeint System.IntPtr
unativeint System.UIntPtr

Można na przykład dodać adnotację do niepodpisanej liczby całkowitej w następujący sposób:

[<Measure>]
type days

let better_age = 3u<days>

Dodanie niepodpisanych typów liczb całkowitych do tej funkcji jest udokumentowane w F# RFC FS-1091.

Wstępnie zdefiniowane jednostki miary

Biblioteka jednostek jest dostępna w FSharp.Data.UnitSystems.SI przestrzeni nazw. Obejmuje ona jednostki SI w postaci symboli (na m przykład dla miernika) w UnitSymbols przestrzeni nazw podrzędnych i w pełnej nazwie (na przykład meter dla miernika) w przestrzeni nazw podrzędnych UnitNames .

Korzystanie z jednostek ogólnych

Możesz napisać funkcje ogólne, które działają na danych, które mają skojarzona jednostka miary. W tym celu należy określić typ razem z jednostką ogólną jako parametr typu, jak pokazano w poniższym przykładzie kodu.

// Distance, meters.
[<Measure>] type m
// Time, seconds.
[<Measure>] type s

let genericSumUnits ( x : float<'u>) (y: float<'u>) = x + y

let v1 = 3.1<m/s>
let v2 = 2.7<m/s>
let x1 = 1.2<m>
let t1 = 1.0<s>

// OK: a function that has unit consistency checking.
let result1 = genericSumUnits v1 v2
// Error reported: mismatched units.
// Uncomment to see error.
// let result2 = genericSumUnits v1 x1

Tworzenie typów kolekcji przy użyciu jednostek ogólnych

Poniższy kod pokazuje, jak utworzyć typ agregujący składający się z poszczególnych wartości zmiennoprzecinkowych, które mają jednostki ogólne. Dzięki temu można utworzyć pojedynczy typ, który działa z różnymi jednostkami. Ponadto jednostki ogólne zachowują bezpieczeństwo typu, zapewniając, że typ ogólny, który ma jeden zestaw jednostek, jest innym typem niż ten sam typ ogólny z innym zestawem jednostek. Podstawą tej techniki jest to, że Measure atrybut można zastosować do parametru typu.

 // Distance, meters.
[<Measure>] type m
// Time, seconds.
[<Measure>] type s

// Define a vector together with a measure type parameter.
// Note the attribute applied to the type parameter.
type vector3D<[<Measure>] 'u> = { x : float<'u>; y : float<'u>; z : float<'u>}

// Create instances that have two different measures.
// Create a position vector.
let xvec : vector3D<m> = { x = 0.0<m>; y = 0.0<m>; z = 0.0<m> }
// Create a velocity vector.
let v1vec : vector3D<m/s> = { x = 1.0<m/s>; y = -1.0<m/s>; z = 0.0<m/s> }

Jednostki w czasie wykonywania

Jednostki miary są używane do sprawdzania typów statycznych. Gdy wartości zmiennoprzecinkowe są kompilowane, jednostki miary zostaną wyeliminowane, więc jednostki zostaną utracone w czasie wykonywania. W związku z tym każda próba zaimplementowania funkcji, która zależy od sprawdzania jednostek w czasie wykonywania, nie jest możliwa. Na przykład zaimplementowanie ToString funkcji w celu wydrukowania jednostek nie jest możliwe.

Konwersje

Aby przekonwertować typ zawierający jednostki (na przykład ) na typ, float<'u>który nie ma jednostek, można użyć standardowej funkcji konwersji. Na przykład można użyć float polecenia , aby przekonwertować na float wartość, która nie ma jednostek, jak pokazano w poniższym kodzie.

[<Measure>]
type cm
let length = 12.0<cm>
let x = float length

Aby przekonwertować wartość bezjednostki na wartość zawierającą jednostki, można pomnożyć wartość 1 lub 1,0, która jest oznaczona za pomocą odpowiednich jednostek. Jednak w przypadku pisania warstw współdziałania istnieją również pewne jawne funkcje, których można użyć do konwertowania wartości bezjednostki na wartości z jednostkami. Znajdują się one w module FSharp.Core.LanguagePrimitives . Aby na przykład przekonwertować element z bezjednostki float na float<cm>element , użyj metody FloatWithMeasure, jak pokazano w poniższym kodzie.

open Microsoft.FSharp.Core
let height:float<cm> = LanguagePrimitives.FloatWithMeasure x

Zobacz też