Eenheden
Drijvende komma en ondertekende geheel getalwaarden in F# kunnen gekoppelde maateenheden hebben, die doorgaans worden gebruikt om lengte, volume, massa, enzovoort aan te geven. Door hoeveelheden met eenheden te gebruiken, stelt u de compiler in staat om te controleren of rekenkundige relaties de juiste eenheden hebben, waardoor programmeerfouten worden voorkomen.
Notitie
Deze voorbeelden laten de juistheid zien in rekenkundige berekeningen met eenheden, de functie kan ook worden gebruikt voor het toevoegen van veilige aantekening van het type met nul representatiekosten aan andere typen, met een benadering zoals FSharp.UMX-project .
Syntaxis
[<Measure>] type unit-name [ = measure ]
Opmerkingen
De vorige syntaxis definieert de eenheidsnaam als een maateenheid. Het optionele onderdeel wordt gebruikt om een nieuwe meting te definiëren in termen van eerder gedefinieerde eenheden. Met de volgende regel wordt bijvoorbeeld de meting cm
(centimeter) gedefinieerd.
[<Measure>] type cm
De volgende regel definieert de meting ml
(milliliter) als een kubieke centimeter (cm^3
).
[<Measure>] type ml = cm^3
In de vorige syntaxis is meting een formule die eenheden omvat. In formules met eenheden worden integrale krachten ondersteund (positief en negatief), geven spaties tussen eenheden een product van de twee eenheden aan, *
geeft ook een product van eenheden aan en /
geeft een quotiënt van eenheden aan. Voor een wederkerige eenheid kunt u een negatief geheel getal gebruiken of een /
waarde die een scheiding aangeeft tussen de teller en noemer van een eenheidsformule. Meerdere eenheden in de noemer moeten tussen haakjes worden omgeven. Eenheden gescheiden door spaties na een /
worden geïnterpreteerd als onderdeel van de noemer, maar alle eenheden na een *
worden geïnterpreteerd als onderdeel van de teller.
U kunt 1 gebruiken in eenheidsexpressies, alleen om een dimensieloze hoeveelheid aan te geven, of samen met andere eenheden, zoals in de teller. De eenheden voor een tarief worden bijvoorbeeld geschreven als 1/s
, waarbij s
seconden worden aangegeven. Haakjes worden niet gebruikt in eenheidsformules. U geeft geen numerieke conversieconstanten op in de eenheidsformules; U kunt conversieconstanten echter afzonderlijk definiëren met eenheden en deze gebruiken in berekeningen die zijn gecontroleerd op eenheden.
Eenheidsformules die hetzelfde betekenen, kunnen op verschillende equivalente manieren worden geschreven. Daarom converteert de compiler eenheidsformules naar een consistente vorm, die negatieve machten converteert naar wederkerigheid, groepeert eenheden in één teller en een noemer, en alfabetiseert de eenheden in de teller en noemer.
De eenheidsformules kg m s^-2
en m /s s * kg
worden bijvoorbeeld beide geconverteerd naar kg m/s^2
.
U gebruikt maateenheden in drijvendekomma-expressies. Als u drijvendekommagetallen samen met gekoppelde maateenheden gebruikt, wordt een ander niveau van typeveiligheid toegevoegd en wordt voorkomen dat de fouten die niet overeenkomen in formules optreden wanneer u zwak getypte drijvendekommanummers gebruikt. Als u een drijvendekomma-expressie schrijft die gebruikmaakt van eenheden, moeten de eenheden in de expressie overeenkomen.
U kunt aantekeningen toevoegen aan letterlijke waarden met een eenheidsformule tussen punthaken, zoals wordt weergegeven in de volgende voorbeelden.
1.0<cm>
55.0<miles/hour>
U plaatst geen spatie tussen het getal en de punthaak; U kunt echter een letterlijk achtervoegsel zoals , zoals f
in het volgende voorbeeld, opnemen.
// The f indicates single-precision floating point.
55.0f<miles/hour>
Een dergelijke aantekening verandert het type van de letterlijke letterlijke tekst van het primitieve type (zoals float
) in een dimensietype, zoals float<cm>
of, in dit geval, float<miles/hour>
. Een eenheidsaantekening van <1>
geeft een dimensieloze hoeveelheid aan en het bijbehorende type is gelijk aan het primitieve type zonder eenheidsparameter.
Het type maateenheid is een drijvende komma of ondertekend integraal type, samen met een extra eenheidsaantekening, aangegeven tussen vierkante haken. Wanneer u dus het type conversie schrijft van g
(gram) naar kg
(kilogram), beschrijft u de typen als volgt.
let convertg2kg (x : float<g>) = x / 1000.0<g/kg>
Maateenheden worden gebruikt voor het controleren van compileertijdeenheden, maar worden niet bewaard in de runtime-omgeving. Daarom hebben ze geen invloed op de prestaties.
Maateenheden kunnen worden toegepast op elk type, niet alleen op drijvendekommatypen; Alleen typen drijvende komma's, ondertekende integrale typen en decimale typen ondersteunen dimensiehoeveelheden. Daarom is het alleen zinvol om maateenheden te gebruiken voor de primitieve typen en op aggregaties die deze primitieve typen bevatten.
In het volgende voorbeeld ziet u het gebruik van maateenheden.
// 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
In het volgende codevoorbeeld ziet u hoe u converteert van een dimensieloos drijvendekommagetal naar een gedimensioneerde drijvende-kommawaarde. U vermenigvuldigt met 1,0 en past de dimensies toe op de 1,0. U kunt dit abstraheren in een functie zoals degreesFahrenheit
.
Wanneer u dimensiewaarden doorgeeft aan functies die dimensieloze drijvendekommanummers verwachten, moet u de eenheden annuleren of casten float
met behulp van de float
operator. In dit voorbeeld deelt u door 1.0<degC>
voor de argumenten, printf
omdat printf
er dimensieloze hoeveelheden worden verwacht.
[<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."
In de volgende voorbeeldsessie ziet u de uitvoer van en invoer in deze code.
Enter a temperature in degrees Fahrenheit.
90
That temperature in degrees Celsius is 32.22.
Primitieve typen die maateenheden ondersteunen
De volgende typen of type afkortingaliassen ondersteunen aantekeningen per maateenheid:
F#-alias | CLR-type |
---|---|
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 |
U kunt bijvoorbeeld als volgt aantekeningen maken op een niet-ondertekend geheel getal:
[<Measure>]
type days
let better_age = 3u<days>
De toevoeging van niet-ondertekende integertypen aan deze functie wordt beschreven in F# RFC FS-1091.
Vooraf gedefinieerde maateenheden
Er is een eenheidsbibliotheek beschikbaar in de FSharp.Data.UnitSystems.SI
naamruimte. Het bevat SI-eenheden in zowel hun symboolvorm (zoals m
voor meter) in de UnitSymbols
subnaamruimte en in hun volledige naam (zoals meter
voor meter) in de UnitNames
subnaamruimte.
Algemene eenheden gebruiken
U kunt algemene functies schrijven die werken op gegevens met een bijbehorende maateenheid. U doet dit door een type op te geven in combinatie met een algemene eenheid als een typeparameter, zoals wordt weergegeven in het volgende codevoorbeeld.
// 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
Verzamelingstypen maken met algemene eenheden
De volgende code laat zien hoe u een aggregaattype maakt dat bestaat uit afzonderlijke drijvendekommawaarden met eenheden die algemeen zijn. Hierdoor kan één type worden gemaakt dat werkt met een verscheidenheid aan eenheden. Daarnaast behouden algemene eenheden de typeveiligheid door ervoor te zorgen dat een algemeen type met één set eenheden een ander type is dan hetzelfde algemene type met een andere set eenheden. De basis van deze techniek is dat het Measure
kenmerk kan worden toegepast op de typeparameter.
// 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> }
Eenheden tijdens runtime
Maateenheden worden gebruikt voor het controleren van statische typen. Wanneer drijvendekommagewaarden worden gecompileerd, worden de maateenheden geëlimineerd, zodat de eenheden verloren gaan tijdens runtime. Daarom is een poging om functionaliteit te implementeren die afhankelijk is van het controleren van de eenheden tijdens runtime niet mogelijk. Het implementeren van een ToString
functie om de eenheden af te drukken is bijvoorbeeld niet mogelijk.
Conversies
Als u een type met eenheden (bijvoorbeeld float<'u>
) wilt converteren naar een type dat geen eenheden heeft, kunt u de standaardconversiefunctie gebruiken. U kunt bijvoorbeeld gebruiken float
om te converteren naar een float
waarde die geen eenheden bevat, zoals wordt weergegeven in de volgende code.
[<Measure>]
type cm
let length = 12.0<cm>
let x = float length
Als u een eenheidloze waarde wilt converteren naar een waarde met eenheden, kunt u vermenigvuldigen met een 1 of 1,0-waarde die is geannoteerd met de juiste eenheden. Voor het schrijven van interoperabiliteitslagen zijn er echter ook enkele expliciete functies die u kunt gebruiken om eenheidsloze waarden te converteren naar waarden met eenheden. Deze bevinden zich in de module FSharp.Core.LanguagePrimitives . Als u bijvoorbeeld wilt converteren van een eenheidloos float
naar een float<cm>
, gebruikt u FloatWithMeasure, zoals wordt weergegeven in de volgende code.
open Microsoft.FSharp.Core
let height:float<cm> = LanguagePrimitives.FloatWithMeasure x