Compartir a través de


Unidades de medida (F#)

En F#, los valores de punto flotante y los valores enteros con signo pueden tener asociadas unidades de medida, que se suelen usar para indicar la longitud, el volumen, la masa, etc. Al usar cantidades con unidades, el compilador puede comprobar si las relaciones aritméticas tienen las unidades correctas, lo que ayuda a evitar errores de programación.

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

Comentarios

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

[<Measure>] type cm

La línea siguiente define la medida ml (mililitro) un como centímetro cúbico (cm^3).

[<Measure>] type ml = cm^3

En la sintaxis anterior, measure es una fórmula que implica el uso de unidades. En las fórmulas que requieren unidades, se admite el uso de potencias enteras (positivas y negativas), los espacios entre unidades indican un producto de las dos unidades, * también indica un producto de unidades y / indica un cociente de unidades. Para una unidad inversa, se puede usar una potencia entera negativa o /, que indica una separación entre el numerador y el denominador de una fórmula de unidades. Si hay varias unidades en el denominador, dichas unidades deben ir entre paréntesis. Las unidades separadas por espacios después de / se interpretan como parte del denominador, pero las unidades después de * se interpretan como parte del numerador.

Se puede usar 1 en las expresiones de unidad, ya sea solo, para indicar una cantidad sin dimensiones, o junto con otras unidades, como en el numerador. Por ejemplo, las unidades para una velocidad se escribirán como 1/s, donde s indica los segundos. No se usan paréntesis en las fórmulas de unidades. No se especifican constantes de conversión numérica en las fórmulas de unidades; sin embargo, se pueden definir constantes de conversión con unidades de forma independiente y utilizarlas en los cálculos con comprobación de unidades.

Las fórmulas de unidades que significan lo mismo pueden escribirse de diferentes formas equivalentes. Por consiguiente, el compilador convierte dichas fórmulas en un formato coherente, de modo que las potencias negativas se convierten en inversas, las unidades se agrupan en un solo numerador y denominador, y se ordenan alfabéticamente en el numerador y el denominador.

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

Las unidades de medida se usan en expresiones de punto flotante. Al utilizar los números de punto flotante con unidades de medida asociadas, se agrega otro nivel de seguridad de tipos y se contribuye a evitar los errores de unidades no coincidentes que se pueden producir en las fórmulas cuando se usan números de punto flotante con tipos flexibles. Si se escribe una expresión de punto flotante que incluye unidades, las unidades de la expresión deben coincidir.

Con una fórmula de unidades, se pueden anotar literales entre corchetes angulares, tal y como se muestra en los ejemplos siguientes.

1.0<cm>
55.0<miles/hour>

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

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

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

El tipo de una unidad de medida es un tipo de punto flotante o un tipo entero con signo junto con una anotación de unidad adicional, que aparece entre corchetes. Por consiguiente, cuando se escribe el tipo de una conversión de g (gramos) en kg (kilogramos), se describen los tipos de la siguiente manera.

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

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

Las unidades de medida se pueden aplicar a cualquier tipo y 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 con dimensiones. Por consiguiente, solo tiene sentido utilizar unidades de medida con los tipos primitivos y los agregados que contengan estos tipos primitivos.

En el siguiente ejemplo se muestra el uso de las 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 

// 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 con dimensiones. Se realiza simplemente una multiplicación por 1,0, aplicando las dimensiones a 1,0. Esto se puede abstraer en una función como degreesFahrenheit.

Además, al pasar valores con dimensiones a funciones que esperan números de punto flotante sin dimensiones, es preciso anular las unidades o convertirlas en float mediante el operador float. En este ejemplo se realiza una división entre 1.0<degC> para los argumentos de printf porque printf espera 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 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."

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

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

Usar unidades genéricas

Se pueden escribir funciones genéricas para usarlas con datos que tienen una unidad de medida asociada. Para ello, se especifica un tipo junto con una unidad genérica como parámetro de tipo, tal y como se especifica en el siguiente ejemplo de código.

// 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 agregados con unidades genéricas

En el siguiente código, se muestra cómo crear un tipo agregado que se compone de valores de punto flotante individuales que tienen unidades genéricas. Esto permite crear un solo tipo que funciona con varias unidades. Además, las unidades genéricas conservan la seguridad de tipos, ya que garantizan que un tipo genérico con un conjunto de unidades determinado es distinto al mismo tipo genérico con otro conjunto de unidades. La base de esta técnica reside en que el atributo Measure puede aplicarse 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 utilizan para la comprobación de tipos en tiempo de compilación. Cuando se compilan los valores de punto flotante, se eliminan las unidades de medida, por lo que se pierden en tiempo de ejecución. Por consiguiente, cualquier intento de implementar funcionalidades que dependen de la comprobación de unidades en tiempo de ejecución generará un error. Por ejemplo, no es posible implementar una función ToString para imprimir las unidades.

Conversiones

Para convertir un tipo que tenga unidades (por ejemplo, float<'u>) en un tipo que no tenga unidades, puede utilizar la función de conversión estándar. Por ejemplo, puede usar float para convertir float en un valor que no tenga 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 unidades en un valor con unidades, puede multiplicar por el valor 1 ó 1,0 al que se hayan agregado las unidades adecuadas. Sin embargo, para escribir niveles de interoperabilidad, también existen algunas funciones explícitas que se pueden utilizar para convertir los valores sin unidades en valores con unidades. Se encuentran en el módulo Microsoft.FSharp.Core.LanguagePrimitives. Por ejemplo, para convertir de float sin unidades en float<cm>, utilice FloatWithMeasure, como se muestra en el código siguiente.

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

Unidades de medida en F# Power Pack

Hay una biblioteca de unidades disponible en F# PowerPack. Esta biblioteca incluye unidades del SI y constantes físicas.

Vea también

Otros recursos

Referencia del lenguaje F#