Unità di misura

I valori integer a virgola mobile e con segno in F# possono avere unità di misura associate, che vengono in genere usate per indicare lunghezza, volume, massa e così via. Usando le quantità con le unità, è possibile abilitare il compilatore per verificare che le relazioni aritmetiche abbiano le unità corrette, in modo da evitare errori di programmazione.

Nota

Questi esempi illustrano la correttezza nei calcoli aritmetici che coinvolgono unità di misura, la funzionalità può anche essere sfruttata per aggiungere annotazione sicura dei tipi con costi di rappresentazione zero ad altri tipi, con approccio come il progetto FSharp.UMX .

Sintassi

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

Osservazioni:

La sintassi precedente definisce unit-name come unità di misura. La parte facoltativa viene usata per definire una nuova misura in termini di unità definite in precedenza. Ad esempio, la riga seguente definisce la misura cm (centimetro).

[<Measure>] type cm

La riga seguente definisce la misura ml (milliliter) come centimetro cubico (cm^3).

[<Measure>] type ml = cm^3

Nella sintassi precedente, measure è una formula che coinvolge le unità. Nelle formule che coinvolgono unità, le potenze integrali sono supportate (positive e negative), gli spazi tra le unità indicano un prodotto delle due unità, * indica anche un prodotto di unità e / indica un quoziente di unità. Per un'unità reciproca, è possibile usare una potenza integer negativa o un valore / che indica una separazione tra il numeratore e il denominatore di una formula di unità. Più unità nel denominatore devono essere racchiuse tra parentesi. Le unità separate da spazi dopo un / oggetto vengono interpretate come parte del denominatore, ma qualsiasi unità successiva a viene * interpretata come parte del numeratore.

È possibile usare 1 nelle espressioni di unità, da solo per indicare una quantità senza dimensione o insieme ad altre unità, ad esempio nel numeratore. Ad esempio, le unità per una frequenza verranno scritte come 1/s, dove s indica i secondi. Le parentesi non vengono usate nelle formule di unità. Non si specificano costanti di conversione numeriche nelle formule di unità; Tuttavia, è possibile definire costanti di conversione con unità separatamente e usarle nei calcoli unit-check.

Formule di unità che indicano che la stessa cosa può essere scritta in diversi modi equivalenti. Pertanto, il compilatore converte le formule di unità in una forma coerente, che converte i poteri negativi in reciproche, raggruppa le unità in un singolo numeratore e un denominatore e alfabetizza le unità nel numeratore e nel denominatore.

Ad esempio, le formule kg m s^-2 di unità e m /s s * kg sono entrambe convertite in kg m/s^2.

Le unità di misura vengono usate nelle espressioni a virgola mobile. L'uso di numeri a virgola mobile insieme alle unità di misura associate aggiunge un altro livello di sicurezza dei tipi e consente di evitare errori di mancata corrispondenza delle unità che possono verificarsi nelle formule quando si usano numeri a virgola mobile tipizzata in modo debole. Se si scrive un'espressione a virgola mobile che usa unità, le unità nell'espressione devono corrispondere.

È possibile annotare i valori letterali con una formula di unità tra parentesi angolari, come illustrato negli esempi seguenti.

1.0<cm>
55.0<miles/hour>

Non si inserisce uno spazio tra il numero e la parentesi angolare; Tuttavia, è possibile includere un suffisso letterale, fad esempio , come nell'esempio seguente.

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

Tale annotazione modifica il tipo del valore letterale dal tipo primitivo ( ad esempio float) a un tipo dimensionato, ad esempio float<cm> o , in questo caso . float<miles/hour> Un'annotazione unità di <1> indica una quantità senza dimensione e il relativo tipo è equivalente al tipo primitivo senza un parametro di unità.

Il tipo di un'unità di misura è un tipo integrale a virgola mobile o con segno insieme a un'annotazione di unità aggiuntiva, indicata tra parentesi quadre. Pertanto, quando si scrive il tipo di una conversione da g (grammi) a kg (chilogrammi), si descrivono i tipi come indicato di seguito.

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

Le unità di misura vengono usate per il controllo unità in fase di compilazione, ma non vengono mantenute nell'ambiente di runtime. Pertanto, non influiscono sulle prestazioni.

Le unità di misura possono essere applicate a qualsiasi tipo, non solo ai tipi a virgola mobile; Tuttavia, solo i tipi a virgola mobile, i tipi integrali con segno e i tipi decimali supportano le quantità dimensionate. Pertanto, è opportuno usare solo unità di misura sui tipi primitivi e sulle aggregazioni che contengono questi tipi primitivi.

Nell'esempio seguente viene illustrato l'uso di unità di misura.

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

Nell'esempio di codice seguente viene illustrato come eseguire la conversione da un numero a virgola mobile senza dimensione a un valore a virgola mobile con dimensione. Si moltiplica solo per 1,0, applicando le dimensioni a 1,0. È possibile astrarre questa operazione in una funzione come degreesFahrenheit.

Inoltre, quando si passano valori con dimensione a funzioni che prevedono numeri a virgola mobile senza dimensione, è necessario annullare le unità o eseguirne float il cast usando l'operatore float . In questo esempio, si divide 1.0<degC> per per gli argomenti perché printfprintf prevede quantità senza dimensioni.

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

La sessione di esempio seguente mostra gli output da e gli input per questo codice.

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

Tipi primitivi che supportano unità di misura

I tipi o gli alias di abbreviazione del tipo seguenti supportano annotazioni unit-of-measure:

Alias F# Tipo 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

Ad esempio, è possibile annotare un intero senza segno come indicato di seguito:

[<Measure>]
type days

let better_age = 3u<days>

L'aggiunta di tipi integer senza segno a questa funzionalità è documentata in F# RFC FS-1091.

Unità di misura predefinite

Una libreria di unità è disponibile nello spazio dei FSharp.Data.UnitSystems.SI nomi . Include le unità SI nel formato simbolo (ad esempio m per meter) nello UnitSymbols spazio sottonome e nel nome completo (ad esempio meter per meter) nello UnitNames spazio sottonome.

Uso di unità generica

È possibile scrivere funzioni generica che operano sui dati con un'unità di misura associata. A tale scopo, specificare un tipo insieme a un'unità generica come parametro di tipo, come illustrato nell'esempio di codice seguente.

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

Creazione di tipi di raccolta con unità generica

Il codice seguente illustra come creare un tipo di aggregazione costituito da singoli valori a virgola mobile con unità generiche. In questo modo è possibile creare un singolo tipo che funziona con un'ampia gamma di unità. Inoltre, le unità generica mantengono la sicurezza dei tipi assicurando che un tipo generico con un set di unità sia un tipo diverso rispetto allo stesso tipo generico con un set di unità diverso. La base di questa tecnica è che l'attributo Measure può essere applicato al parametro di tipo.

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

Unità in fase di esecuzione

Le unità di misura vengono usate per il controllo dei tipi statici. Quando vengono compilati valori a virgola mobile, le unità di misura vengono eliminate, quindi le unità vengono perse in fase di esecuzione. Pertanto, qualsiasi tentativo di implementare funzionalità che dipende dal controllo delle unità in fase di esecuzione non è possibile. Ad esempio, l'implementazione di una ToString funzione per stampare le unità non è possibile.

Conversioni

Per convertire un tipo con unità (ad esempio, float<'u>) in un tipo che non dispone di unità, è possibile usare la funzione di conversione standard. Ad esempio, è possibile usare float per convertire in un float valore che non dispone di unità, come illustrato nel codice seguente.

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

Per convertire un valore senza unità in un valore con unità di misura, è possibile moltiplicare per un valore 1 o 1,0 annotato con le unità appropriate. Tuttavia, per la scrittura di livelli di interoperabilità, esistono anche alcune funzioni esplicite che è possibile usare per convertire valori unitless in valori con unità di misura. Si trovano nel modulo FSharp.Core.LanguagePrimitives . Ad esempio, per eseguire la conversione da un oggetto unitless float a , float<cm>usare FloatWithMeasure, come illustrato nel codice seguente.

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

Vedi anche