Unidades de medida

Los valores de punto flotante y entero con signo en F# pueden tener unidades de medida asociadas, que normalmente se usan para indicar la longitud, el volumen, la masa, y así sucesivamente. Mediante el uso de cantidades con unidades, permite al compilador comprobar que las relaciones aritméticas tienen las unidades correctas, lo que ayuda a evitar errores de programación.

Sintaxis

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

Comentarios

La sintaxis anterior define unit-name como una unidad de medida. La parte opcional se usa para definir una nueva medida en términos de unidades definidas previamente. Por ejemplo, en la línea siguiente se define la medida cm (centímetros).

[<Measure>] type cm

En la línea siguiente se define la medida ml (milliter) como un centímetros cúbica ( cm^3 ).

[<Measure>] type ml = cm^3

En la sintaxis anterior, measure es una fórmula que implica unidades. En las fórmulas que implican unidades, se admiten potencias integrales (positivas y negativas), los espacios entre unidades indican un producto de las dos unidades, también indica un producto de unidades e indica un cociente de * / unidades. Para una unidad recíproca, puede usar una potencia de entero negativo o una que indica una separación entre el numerador y / el denominador de una fórmula de unidad. Varias unidades del denominador deben ir entre paréntesis. Las unidades separadas por espacios después de una se interpretan como parte del denominador, pero las unidades siguientes a se interpretan como parte / * del numerador.

Puede usar 1 en expresiones unitarias, ya sea solo para indicar una cantidad sin dimensiones, o junto con otras unidades, como en el numerador. Por ejemplo, las unidades de una velocidad se escribirían como 1/s , donde s indica segundos. Los paréntesis no se usan en fórmulas unitarias. No se especifican constantes de conversión numérica en las fórmulas de unidad; sin embargo, puede definir constantes de conversión con unidades por separado y usarlas en cálculos controlados por unidades.

Las fórmulas unitarias que significan lo mismo se pueden escribir de varias maneras equivalentes. Por lo tanto, el compilador convierte las fórmulas de unidad en una forma coherente, que convierte potencias negativas en recíprocos, agrupa las unidades en un solo numerador y un denominador, y alfabéticamente las unidades del numerador y denominador.

Por ejemplo, las fórmulas unitarias kg m s^-2 y m /s s * kg se convierten en kg m/s^2 .

Las unidades de medida se usan en expresiones de punto flotante. El uso de números de punto flotante junto con unidades de medida asociadas agrega otro nivel de seguridad de tipos y ayuda a evitar los errores de coincidencia de unidades que pueden producirse en fórmulas cuando se usan números de punto flotante con tipos débiles. Si escribe una expresión de punto flotante que usa unidades, las unidades de la expresión deben coincidir.

Puede anotar literales con una fórmula de unidad entre corchetes angulares, como se muestra en los ejemplos siguientes.

1.0<cm>
55.0<miles/hour>

No coloque un espacio entre el número y el corchete angular; sin embargo, puede incluir un sufijo literal como f , como en el ejemplo siguiente.

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

Esta anotación cambia el tipo del literal de su tipo primitivo (como ) a un tipo dimensionado, como float float<cm> o, en este caso, float<miles/hour> . Una anotación de unidad de indica una cantidad sin dimensiones y su tipo es equivalente al <1> tipo primitivo sin un parámetro de unidad.

El tipo de una unidad de medida es un tipo entero de punto flotante o con firma junto con una anotación de unidad adicional, indicada entre corchetes. Por lo tanto, al escribir el tipo de una conversión g de (gramas) a kg (ruedas), se describen los tipos de la manera siguiente.

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

Las unidades de medida se usan para la comprobación de unidades en tiempo de compilación, pero no se conservan en el entorno en tiempo de ejecución. Por lo tanto, no afectan al rendimiento.

Las unidades de medida se pueden aplicar a cualquier tipo, no solo a los tipos de punto flotante; sin embargo, solo los tipos de punto flotante, los tipos enteros con signo y los tipos decimales admiten cantidades dimensionadas. Por lo tanto, solo tiene sentido usar unidades de medida en los tipos primitivos y en agregados que contienen estos tipos primitivos.

En el ejemplo siguiente se muestra el uso de unidades de medida.

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

En el ejemplo de código siguiente se muestra cómo convertir un número de punto flotante sin dimensiones en un valor de punto flotante dimensionado. Simplemente se multiplica por 1.0, aplicando las dimensiones a 1.0. Puede abstraer esto en una función como degreesFahrenheit .

Además, al pasar valores dimensionados a funciones que esperan números de punto flotante sin dimensiones, debe cancelar las unidades o convertir a float mediante el float operador . En este ejemplo, se divide por 1.0<degC> para los argumentos a printf porque espera printf cantidades sin dimensiones.

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

En la sesión de ejemplo siguiente se muestran las salidas de y las entradas de este código.

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

Tipos primitivos que admiten unidades de medida

Los siguientes tipos o alias de abreviatura de tipo admiten anotaciones de unidad de medida:

Alias de 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

Por ejemplo, puede anotar un entero sin signo como se indica a continuación:

[<Measure>]
type days

let better_age = 3u<days>

La adición de tipos enteros sin signo a esta característica se documenta en F# RFC FS-1091.

Unidades de medida predefinidas

Hay una biblioteca de unidades disponible en el espacio de FSharp.Data.UnitSystems.SI nombres . Incluye unidades de SI en su forma de símbolo (como para el medidor) en el subespacio de nombres y en su nombre completo (como para el medidor) en el m UnitSymbols meter UnitNames subespacio de subnombres.

Uso de unidades genéricas

Puede escribir funciones genéricas que funcionen en datos que tienen asociada una unidad de medida. Para ello, especifique un tipo junto con una unidad genérica como parámetro de tipo, como se muestra en el ejemplo de código siguiente.

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

Crear tipos de colección con unidades genéricas

El código siguiente muestra cómo crear un tipo de agregado que consta de valores de punto flotante individuales que tienen unidades genéricas. Esto permite crear un único tipo que funciona con una variedad de unidades. Además, las unidades genéricas conservan la seguridad de tipos asegurándose de que un tipo genérico que tiene un conjunto de unidades es un tipo diferente que el mismo tipo genérico con un conjunto de unidades diferente. La base de esta técnica es que el Measure atributo se puede aplicar al parámetro de 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> }

Unidades en tiempo de ejecución

Las unidades de medida se usan para la comprobación de tipos estáticos. Cuando se compilan los valores de punto flotante, se eliminan las unidades de medida, por lo que las unidades se pierden en tiempo de ejecución. Por lo tanto, no es posible cualquier intento de implementar una funcionalidad que dependa de la comprobación de las unidades en tiempo de ejecución. Por ejemplo, no es posible ToString implementar una función para imprimir las unidades.

Conversiones

Para convertir un tipo que tiene unidades (por ejemplo, ) en un tipo que no tiene unidades, puede usar float<'u> la función de conversión estándar. Por ejemplo, puede usar float para convertir en un valor que no tiene float unidades, como se muestra en el código siguiente.

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

Para convertir un valor sin unidad en un valor que tenga unidades, puede multiplicar por un valor de 1 o 1,0 anotado con las unidades adecuadas. Sin embargo, para escribir capas de interoperabilidad, también hay algunas funciones explícitas que puede usar para convertir valores sin unidad en valores con unidades. Se encuentran en el módulo FSharp.Core.LanguagePrimitives. Por ejemplo, para convertir de un sin unidad float a un , use float<cm> FloatWithMeasure, como se muestra en el código siguiente.

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

Vea también