Unités de mesure (F#)
En F#, les valeurs à virgule flottante et les valeurs d'entiers signés peuvent être associées à des unités de mesure. Celles-ci sont généralement utilisées pour indiquer la longueur, le volume, la masse, etc. Lorsque vous utilisez des quantités avec des unités, le compilateur est en mesure de vérifier que les relations arithmétiques ont les bonnes unités, ce qui permet d'éviter les erreurs de programmation.
[<Measure>] type unit-name [ = measure ]
Notes
La syntaxe précédente définit unit-name comme unité de mesure. La partie facultative est utilisée pour définir une nouvelle mesure en termes d'unités précédemment définies. Par exemple, la ligne suivante définit la mesure cm (centimètre).
[<Measure>] type cm
La ligne suivante définit la mesure ml (millilitre) comme un centimètre cube (cm^3).
[<Measure>] type ml = cm^3
Dans la syntaxe précédente, measure est une formule qui implique des unités. Dans les formules qui impliquent des unités, les puissances intégrales sont prises en charge (positives et négatives), les espaces entre les unités indiquent un produit des deux unités, * indique également un produit d'unités, et / indique un quotient d'unités. Pour une unité réciproque, vous pouvez utiliser une puissance entière négative ou un / qui indique une séparation entre le numérateur et le dénominateur d'une formule d'unité. Si le dénominateur comprend plusieurs unités, il est recommandé de les mettre entre parenthèses. Les unités séparées par des espaces après / sont interprétées comme faisant partie du dénominateur, tandis que les unités situées après * sont interprétées comme faisant partie du numérateur.
Vous pouvez utiliser 1 dans des expressions d'unité, soit seul pour indiquer une quantité sans dimension, soit avec d'autres unités, comme dans le numérateur. Par exemple, les unités pour un taux sont écrites 1/s, où s indique ses secondes. Les parenthèses ne sont pas utilisées dans les formules d'unité. Vous ne spécifiez pas de constantes de conversion numériques dans les formules d'unité ; toutefois, vous pouvez définir séparément des constantes de conversion avec des unités, et les utiliser dans les calculs avec vérification des unités.
Les formules d'unité qui signifient la même chose peuvent être écrites de plusieurs façons équivalentes. Par conséquent, le compilateur convertit les formules d'unité dans un format cohérent ; il convertit les puissances négatives en réciproques, groupe les unités dans un numérateur unique et un dénominateur, et classe les unités dans le numérateur et le dénominateur par ordre alphabétique.
Par exemple, les formules d'unité kg m s^-2 et m /s s * kg sont converties en kg m/s^2.
Vous utilisez des unités de mesure dans les expressions à virgule flottante. L'utilisation de nombres à virgule flottante avec des unités de mesure associées ajoute un autre niveau de sécurité de type, et permet d'éviter les erreurs d'incompatibilité d'unité pouvant se produire dans les formules lorsque vous utilisez des nombres à virgule flottante faiblement typés. Si vous écrivez une expression à virgule flottante qui utilise des unités, les unités dans l'expression doivent correspondre.
Vous pouvez annoter des littéraux avec une formule d'unité dans des crochets pointus, comme indiqué dans les exemples suivants.
1.0<cm>
55.0<miles/hour>
Ne tapez pas d'espace entre le nombre et le crochet pointu ; toutefois, vous pouvez inclure un suffixe littéral, tel que f, comme dans l'exemple suivant.
// The f indicates single-precision floating point.
55.0f<miles/hour>
Une telle annotation modifie le type du littéral de son type primitif (tel que float) en un type dimensionné, par exemple float<cm> ou, dans ce cas, float<miles/hour>. Une annotation d'unité <1> indique une quantité sans dimension, et son type équivaut au type primitif sans paramètre d'unité.
Le type d'une unité de mesure est un type à virgule flottante ou un type intégral signé avec une annotation d'unité supplémentaire, indiquée entre crochets. Ainsi, lorsque vous écrivez le type d'une conversion de g (grammes) en kg (kilogrammes), vous décrivez les types comme suit.
let convertg2kg (x : float<g>) = x / 1000.0<g/kg>
Les unités de mesure sont utilisées pour la vérification des unités au moment de la compilation, mais elles ne sont pas persistantes dans l'environnement d'exécution. Elles n'affectent donc pas les performances.
Les unités de mesure peuvent être appliquées à n'importe quel type, pas simplement aux types à virgule flottante ; toutefois, seuls les types à virgule flottante, les types intégraux signés et les types décimaux prennent en charge les quantités dimensionnées. Par conséquent, l'utilisation d'unités de mesure ne présente d'intérêt que sur les types primitifs et les agrégats qui contiennent ces types primitifs.
L'exemple suivant illustre l'utilisation d'unités de mesure.
// 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
L'exemple de code suivant illustre comment convertir un nombre à virgule flottante sans dimension en une valeur à virgule flottante dimensionnée. Vous multipliez simplement par 1.0, en appliquant les dimensions à la valeur 1.0. Vous pouvez faire de ceci une fonction abstract, par exemple degreesFahrenheit.
Par ailleurs, lorsque vous passez des valeurs dimensionnées à des fonctions qui attendent des nombres à virgule flottante sans dimension, vous devez annuler les unités ou caster en float à l'aide de l'opérateur float. Dans cet exemple, vous divisez par 1.0<degC> pour les arguments passés à printf, car printf attend des quantités sans dimension.
[<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."
L'exemple de session suivant présente les entrées et sorties de ce code.
Enter a temperature in degrees Fahrenheit.
90
That temperature in degrees Celsius is 32.22.
Utilisation d'unités génériques
Vous pouvez écrire des fonctions génériques qui fonctionnent sur des données associées à une unité de mesure. Pour cela, spécifiez un type avec une unité générique comme paramètre de type, comme indiqué dans l'exemple de code suivant.
// 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
Création de types d'agrégat avec des unités génériques
Le code suivant montre comment créer un type d'agrégation composé de valeurs à virgule flottante individuelles avec des unités génériques. Cela permet de créer un type unique qui fonctionne avec des unités diverses. De plus, la sécurité de type est préservée, car les unités génériques vérifient qu'un type générique ayant un jeu d'unités est différent du même type générique avec un autre jeu d'unités. Cette technique repose sur le fait que l'attribut Measure peut être appliqué au paramètre de type.
// 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és au moment de l'exécution
Les unités de mesure sont utilisées pour la vérification de type statique. Lorsque des valeurs à virgule flottante sont compilées, les unités de mesure sont éliminées ; ainsi, au moment de l'exécution, les unités sont perdues. Par conséquent, il est impossible d'implémenter des fonctionnalités qui dépendent de la vérification des unités au moment de l'exécution. Par exemple, il n'est pas possible d'implémenter une fonction ToString pour imprimer les unités.
Conversions
Pour convertir un type avec unités (par exemple, float<'u>) en un type sans unité, vous pouvez utiliser la fonction de conversion standard. Par exemple, vous pouvez utiliser float pour convertir en une valeur float n'ayant pas d'unités, comme illustré dans le code suivant.
[<Measure>]
type cm
let length = 12.0<cm>
let x = float length
Pour convertir une valeur sans unité en une valeur ayant des unités, vous pouvez multiplier par une valeur 1 ou 1.0 annotée avec les unités appropriées. Toutefois, pour écrire des couches d'interopérabilité, vous pouvez utiliser certaines fonctions explicites pour convertir des valeurs sans unité en valeurs avec unités. Ces dernières sont dans le module Microsoft.FSharp.Core.LanguagePrimitives. Par exemple, pour convertir un float sans unité à float<cm>, utilisez FloatWithMeasure, comme indiqué dans le code suivant.
open Microsoft.FSharp.Core
let height:float<cm> = LanguagePrimitives.FloatWithMeasure x
Unités de mesure dans le F# Power Pack
Une bibliothèque d'unités est disponible dans le F# PowerPack. La bibliothèque d'unités inclut des unités du système international (SI) et des constantes physiques.