Condividi tramite


Unità di misura (F#)

Ai valori a virgola mobile e interi con segno in F# possono essere associate unità di misura, utilizzate in genere per indicare lunghezza, volume, massa e così via. Utilizzando quantità con unità, è possibile consentire al compilatore di verificare che le relazioni aritmetiche dispongano di unità corrette, prevenendo così eventuali errori di programmazione.

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

Note

Nella sintassi precedente l'elemento unit-name viene definito come un'unità di misura. La parte facoltativa viene utilizzata per definire una nuova misura in termini di unità definite in precedenza. Nella riga seguente viene ad esempio definita la misura cm (centimetro).

[<Measure>] type cm

Nella riga seguente viene definita la misura ml (millimetro) come un centimetro cubo (cm^3).

[<Measure>] type ml = cm^3

Nella sintassi precedente measure è una formula in cui vengono utilizzate unità. Nelle formule in cui vengono utilizzate unità, sono supportate le potenze integrali (positive e negative), gli spazi tra le unità indicano un prodotto delle due unità, * indica un prodotto di unità e / indica un quoziente di unità. Per un'unità reciproca è possibile utilizzare una potenza intera negativa o un simbolo /, che indica una separazione tra il numeratore e il denominatore di una formula di unità. Se nel denominatore sono presenti più unità, devono essere racchiuse tra parentesi. Le unità separate da spazi dopo un simbolo / vengono interpretate come parte del denominatore, ma qualsiasi unità che segue un simbolo * viene interpretata come parte del numeratore.

È possibile utilizzare 1 nelle espressioni di unità, da solo per indicare una quantità senza dimensioni o insieme ad altre unità, ad esempio nel numeratore. Le unità per una frequenza verrebbero ad esempio indicate come 1/s, dove s indica i secondi. Nelle formule di unità non vengono utilizzate parentesi. Nelle formule di unità non vengono specificate costanti di conversione numerica, ma è tuttavia possibile definire costanti di conversione con unità separatamente e utilizzarle nei calcoli con controllo dell'unità.

Le formule di unità con lo stesso significato possono essere scritte in diversi modi equivalenti. Tramite il compilatore, le formule di unità vengono pertanto convertite in un formato coerente, convertendo le potenze negative in reciproci, raggruppando le unità in un unico numeratore e un denominatore e disponendo in ordine alfabetico le unità nel numeratore e nel denominatore.

Le formule di unità kg m s^-2 e m /s s * kg vengono ad esempio convertite entrambe in kg m/s^2.

Le unità di misura vengono utilizzate nelle espressioni a virgola mobile. L'utilizzo dei numeri a virgola mobile insieme alle unità di misura associate consente di aggiungere un ulteriore livello di indipendenza dai tipi e aiuta a evitare gli errori di mancata corrispondenza delle unità che possono verificarsi nelle formule quando si utilizzano numeri a virgola mobile tipizzati in modo debole. Se si scrive un'espressione a virgola mobile in cui vengono utilizzate unità, le unità nell'espressione devono corrispondere.

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

1.0<cm>
55.0<miles/hour>

Tra il numero e la parentesi acuta non devono essere inseriti spazi, tuttavia è possibile includere un suffisso letterale come f, come nell'esempio seguente.

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

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

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

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

Le unità di misura vengono utilizzate per il controllo dell'unità in fase di compilazione ma non vengono salvate in modo permanente nell'ambiente di runtime. Esse pertanto non influiscono sulle prestazioni.

Le unità di misura possono essere applicate a qualsiasi tipo, non solo ai tipi a virgola mobile. Le quantità con dimensioni sono tuttavia supportate solo da tipi a virgola mobile, tipi integrali con segno e tipi decimali. È pertanto opportuno utilizzare le unità di misura solo con i tipi primitivi e le aggregazioni che contengono questi tipi primitivi.

Nell'esempio seguente viene illustrato l'utilizzo delle 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 

// 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 convertire un numero a virgola mobile senza dimensioni in un valore a virgola mobile con dimensioni. È sufficiente moltiplicare per 1,0 con le dimensioni applicate a 1,0. È possibile astrarre questo concetto in una funzione come degreesFahrenheit.

Quando si passano valori con dimensioni a funzioni che prevedono numeri a virgola mobile senza dimensioni, è inoltre necessario eliminare le unità o utilizzare l'operatore float per eseguire il cast nel tipo float. In questo esempio, si esegue la divisione per 1.0<degC> per gli argomenti di printf, in quanto printf 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 mutable floatValue = 0.
if System.Double.TryParse(input, &floatValue)
   then 
      printfn "That temperature in Celsius is %8.2f degrees C." ((convertFtoC (degreesFahrenheit floatValue))/(1.0<degC>))
   else
      printfn "Error parsing input."

Nella sessione di esempio seguente vengono mostrati gli output da e gli input a questo codice.

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

Utilizzo di unità generiche

È possibile scrivere funzioni generiche da utilizzare per i dati a cui è associata un'unità di misura. A tale scopo, è necessario specificare un tipo insieme a un'unità generica come parametro di tipo, come indicato 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 aggregazione con unità generiche

Nel codice seguente viene illustrato come creare un tipo di aggregazione costituito da singoli valori a virgola mobile con unità generiche. In questo modo è possibile creare un unico tipo da utilizzare con diverse unità. Le unità generiche mantengono inoltre l'indipendenza dai tipi, garantendo che un tipo generico che dispone di un set di unità sia un tipo diverso rispetto allo stesso tipo generico con un set diverso di unità. Questa tecnica è basata sul fatto 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 utilizzate per il controllo statico del tipo. Quando vengono compilati valori a virgola mobile, le unità di misura vengono eliminate e pertanto non sono presenti al runtime. Non è quindi possibile effettuare alcun tentativo di implementare funzionalità che dipendano dal controllo delle unità al runtime. Non è ad esempio possibile implementare una funzione ToString per la stampa delle unità.

Conversioni

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

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

Per convertire un valore privo di unità in un valore con unità, è possibile moltiplicare per 1 o 1,0 il valore annotato con le unità appropriate. Tuttavia, per la scrittura di livelli di interoperabilità, esistono anche alcune funzioni esplicite che possono essere utilizzate per la conversione di valori privi di unità in valori con unità. Tali funzioni sono contenute nel modulo Microsoft.FSharp.Core.LanguagePrimitives. Ad esempio, per eseguire la conversione da un valore float privo di unità a un valore float<cm>, utilizzare FloatWithMeasure, come mostrato nell'esempio seguente.

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

Unità di misura in F# Power Pack

In F# PowerPack è disponibile una libreria di unità. La libreria di unità include unità del sistema internazionale e costanti fisiche.

Vedere anche

Altre risorse

Riferimenti per il linguaggio F#

Cronologia delle modifiche

Data

Cronologia

Motivo

Maggio 2010

Correggere l'esempio di codice nella sezione Conversioni.

Correzione di bug nel contenuto.