Compartir a través de


Unidades de medida

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

Nota:

Estos ejemplos muestran la corrección en los cálculos aritméticos que implican unidades de medida, la característica también se puede aprovechar para agregar anotaciones seguras de tipos con cero costos de representación a otros tipos, con un enfoque como el proyecto FSharp.UMX .

Sintaxis

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

Observaciones

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

[<Measure>] type cm

La línea siguiente define la medida ml (mililitro) como un centímetros cúbicos (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 negativa o una / que indique una separación entre el numerador y el denominador de una fórmula unitaria. Varias unidades del denominador deben estar rodeadas por paréntesis. Las unidades separadas por espacios después / de que se interpreten como parte del denominador, pero las unidades que siguen 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 tasa se escribirían como 1/s, donde s indica segundos. Los paréntesis no se usan en fórmulas de unidad. No se especifican constantes de conversión numéricas en las fórmulas de unidad; sin embargo, puede definir constantes de conversión con unidades por separado y usarlas en cálculos comprobados por unidad.

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

Por ejemplo, las fórmulas kg m s^-2 de unidad 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 tipo y ayuda a evitar los errores de coincidencia de unidad que pueden producirse en fórmulas cuando se usan números de punto flotante débilmente tipados. 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 se coloca un espacio entre el número y el corchete angular; sin embargo, puede incluir un sufijo literal como , como fen 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 (por floatejemplo, ) a un tipo con dimensiones, como float<cm> o , en este caso, float<miles/hour>. Una anotación unitaria de <1> indica una cantidad sin dimensiones y su tipo es equivalente al tipo primitivo sin un parámetro de unidad.

El tipo de una unidad de medida es un tipo entero con signo o punto flotante junto con una anotación de unidad adicional, indicada entre corchetes. Por lo tanto, al escribir el tipo de una conversión de g (gramos) a kg (kilogramos), se describen los tipos como se indica a continuación.

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 tipos de punto flotante; sin embargo, solo los tipos de punto flotante, los tipos enteros firmados 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 de un número de punto flotante sin dimensiones a un valor de punto flotante dimensionado. Simplemente se multiplica por 1,0, aplicando las dimensiones a la 1.0. Puede abstraer esto en una función como degreesFahrenheit.

Además, cuando se pasan valores de dimensión 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 , ya printf que 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 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 siguiente sesión de ejemplo 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 unit of measure:

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 de la siguiente manera:

[<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 disponible una biblioteca de unidades en el FSharp.Data.UnitSystems.SI espacio de nombres . Incluye unidades DE SI en su forma de símbolo (como m para medidor) en el UnitSymbols subnombrespace y en su nombre completo (como meter para medidor) en el UnitNames subnombrespace.

Uso de unidades genéricas

Puede escribir funciones genéricas que operan en datos que tienen una unidad de medida asociada. 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

Creación de tipos de colección con unidades genéricas

En el código siguiente se 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 funcione 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 al del mismo tipo genérico con un conjunto diferente de unidades. 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 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, cualquier intento de implementar la funcionalidad que depende de comprobar las unidades en tiempo de ejecución no es posible. Por ejemplo, no es posible implementar una ToString función para imprimir las unidades.

Conversiones

Para convertir un tipo que tenga unidades (por ejemplo, float<'u>) a un tipo que no tenga unidades, puede usar la función de conversión estándar. Por ejemplo, puede usar float para convertir en un float 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 unidad a 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 se pueden usar para convertir valores sin unidad a valores con unidades. Se encuentran en el módulo FSharp.Core.LanguagePrimitives . Por ejemplo, para convertir de un sin float unidad a , float<cm>use FloatWithMeasure, como se muestra en el código siguiente.

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

Consulte también