Měrné jednotky

Hodnoty s plovoucí desetinnou čárkou a znaménkem v jazyce F# můžou mít přidružené měrné jednotky, které se obvykle používají k označení délky, objemu, hmotnosti atd. Pomocí množství s jednotkami umožníte kompilátoru ověřit, že aritmetické relace mají správné jednotky, což pomáhá zabránit programovacím chybám.

Poznámka:

Tyto příklady ukazují správnost aritmetických výpočtů zahrnujících měrné jednotky. Tato funkce se dá využít také k přidání bezpečné poznámky typu s nulovými náklady na reprezentaci do jiných typů s přístupem, jako je projekt FSharp.UMX .

Syntaxe

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

Poznámky

Předchozí syntaxe definuje název jednotky jako měrnou jednotku. Volitelná část slouží k definování nové míry z hlediska dříve definovaných jednotek. Například následující řádek definuje míru cm (centimetr).

[<Measure>] type cm

Následující řádek definuje míru ml (mililiter) jako krychlový centimetr (cm^3).

[<Measure>] type ml = cm^3

V předchozí syntaxi je míra vzorec, který zahrnuje jednotky. Ve vzorcích, které zahrnují jednotky, jsou podporovány integrální mocniny (kladné a záporné), mezery mezi jednotkami označují součin těchto dvou jednotek, * označuje také součin jednotek a / označuje podíl jednotek. Pro reciproční jednotku můžete použít zápornou celočíselnou mocninu nebo / hodnotu označující oddělení mezi čitatelem a jmenovatelem vzorce jednotky. Více jednotek v jmenovateli by mělo být obklopeno závorky. Jednotky oddělené mezerami poté / , co jsou interpretovány jako součást jmenovatele, ale všechny jednotky, které následují * , jsou interpretovány jako součást čitatele.

1 ve výrazech jednotek můžete použít samostatně k označení množství bez dimenzí nebo společně s jinými jednotkami, například v čitatelu. Například jednotky pro sazbu by se napsaly jako 1/s, kde s indikuje sekundy. V jednotkových vzorcích se nepoužívají závorky. Nezadáte číselné konverzní konstanty ve vzorcích jednotek; Můžete však definovat konverzní konstanty s zvlášť jednotkami a použít je ve výpočtech s kontrolou jednotek.

Jednotkové vzorce, které znamenají stejnou věc, se dají napsat různými ekvivalentními způsoby. Kompilátor proto převádí vzorce jednotek na konzistentní formu, která převádí záporné mocniny na reciproční, seskupuje jednotky na jeden čitatel a jmenovatel a abecedizuje jednotky v čitatelu a jmenovatele.

Například vzorce jednotek kg m s^-2 a m /s s * kg oba jsou převedeny na kg m/s^2.

Ve výrazech s plovoucí desetinou čárkou se používají měrné jednotky. Použití čísel s plovoucí desetinou čárkou společně s přidruženými měrnými jednotkami přidává další úroveň bezpečnosti typů a pomáhá vyhnout se chybám neshody jednotek, ke kterým může dojít ve vzorcích při použití slabě zadaných čísel s plovoucí desetinou čárkou. Pokud napíšete výraz s plovoucí desetinou čárkou, který používá jednotky, musí se jednotky ve výrazu shodovat.

Literály můžete komentovat pomocí vzorce jednotky v úhlových závorkách, jak je znázorněno v následujících příkladech.

1.0<cm>
55.0<miles/hour>

Nezadáte mezeru mezi číslem a úhlovou závorkou; můžete však zahrnout literálovou příponu, například f, jako v následujícím příkladu.

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

Taková poznámka změní typ literálu z jeho primitivního typu (například float) na typ dimenze, například float<cm> nebo , v tomto případě , float<miles/hour>. Poznámka <1> jednotky označuje množství bez dimenzí a jeho typ je ekvivalentní primitivnímu typu bez parametru jednotky.

Typ měrné jednotky je celočíselný typ s plovoucí desetinou čárkou nebo podepsaným celočíselným typem spolu s poznámkami k nadbytečné jednotce označené v hranatých závorkách. Když tedy napíšete typ převodu z g (gramů) na kg (kg), popíšete typy následujícím způsobem.

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

Měrné jednotky se používají pro kontrolu jednotek kompilace, ale nejsou trvalé v prostředí za běhu. Proto nemají vliv na výkon.

Měrné jednotky lze použít u libovolného typu, nikoli pouze u typů s plovoucí desetinou čárkou; Pouze typy s plovoucí desetinnou čárkou, celočíselné typy se signepsovanými integrálními typy a desetinnými místy však podporují kótované množství. Proto dává smysl používat pouze jednotky měření na primitivních typech a agregace, které obsahují tyto primitivní typy.

Následující příklad znázorňuje použití měrných jednotek.

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

Následující příklad kódu ukazuje, jak převést z čísla s plovoucí desetinou čárkou bez dimenzí na hodnotu s plovoucí desetinou čárkou. Jednoduše vynásobíte číslem 1,0 a použijete rozměry na 1,0. Můžete to abstrahovat do funkce, jako degreesFahrenheitje .

Pokud také předáte hodnoty dimenzí funkcím, které očekávají čísla s plovoucí desetinou čárkou bez dimenzí, je nutné zrušit jednotky nebo přetypovat float pomocí operátoru float . V tomto příkladu rozdělíte 1.0<degC> argumenty tak, aby printfprintf očekávaly množství bez dimenzí.

[<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."

Následující ukázková relace ukazuje výstupy a vstupy do tohoto kódu.

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

Primitivní typy podporující měrné jednotky

Následující typy nebo aliasy zkratek typů podporují poznámky měrných jednotek:

Alias 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

Můžete například anotovat celé číslo bez znaménka následujícím způsobem:

[<Measure>]
type days

let better_age = 3u<days>

Přidání celočíselného typu bez znaménka k této funkci je zdokumentované v F# RFC FS-1091.

Předdefinované jednotky míry

Knihovna jednotek je k dispozici v FSharp.Data.UnitSystems.SI oboru názvů. Zahrnuje jednotky SI jak ve tvaru symbolu (například m pro měřič), tak UnitSymbols v podnamespace a v celém názvu (například meter pro měřič) v podnázvovém UnitNames prostoru.

Použití obecných jednotek

Můžete napsat obecné funkce, které pracují s daty, která mají přidruženou měrnou jednotku. Uděláte to tak, že zadáte typ společně s obecnou jednotkou jako parametr typu, jak je znázorněno v následujícím příkladu kódu.

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

Vytváření typů kolekcí pomocí obecných jednotek

Následující kód ukazuje, jak vytvořit agregační typ, který se skládá z jednotlivých hodnot s plovoucí desetinou čárkou, které mají obecné jednotky. To umožňuje vytvoření jednoho typu, který funguje s různými jednotkami. Obecné jednotky také zachovávají bezpečnost typů tím, že zajistí, že obecný typ, který má jednu sadu jednotek, je jiný typ než stejný obecný typ s jinou sadou jednotek. Základem této techniky je, že Measure atribut lze použít u 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> }

Jednotky za běhu

Jednotky měr se používají pro kontrolu statického typu. Při kompilaci hodnot s plovoucí desetinou čárkou se jednotky míry eliminují, takže jednotky se ztratí za běhu. Proto není možné provést všechny pokusy o implementaci funkcí, které závisí na kontrole jednotek za běhu. Například implementace ToString funkce pro vytištění jednotek není možná.

Převody

Chcete-li převést typ, který obsahuje jednotky (například float<'u>) na typ, který neobsahuje jednotky, můžete použít standardní převodní funkci. Můžete například použít float převod na float hodnotu, která nemá jednotky, jak je znázorněno v následujícím kódu.

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

Pokud chcete převést jednotkovou hodnotu na hodnotu, která obsahuje jednotky, můžete vynásobit hodnotou 1 nebo 1,0, která je opatřena poznámkami odpovídajícími jednotkami. Pro zápis vrstev interoperability však existují také některé explicitní funkce, které můžete použít k převodu jednotek bez jednotek na hodnoty s jednotkami. Jsou v modulu FSharp.Core.LanguagePrimitives . Pokud chcete například převést z jednotek float na float<cm>, použijte FloatWithMeasure, jak je znázorněno v následujícím kódu.

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

Viz také