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 degreesFahrenheit
je .
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 printf
printf
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