度量单位

F# 中的浮点和带符号整数值可以具有关联的度量单位,这些度量单位通常用于指示长度、卷、质量等。 通过对单位使用数量,使编译器能够验证算术关系是否具有正确的单位,这有助于防止编程错误。

注释

这些示例演示了涉及度量单位的算术计算的正确性,还可以利用该功能将类型安全批注添加到其他类型的零表示成本,方法如 FSharp.UMX 项目。

语法

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

注解

前面的语法将 单位名称 定义为度量单位。 可选部件用于根据以前定义的单位定义新度量值。 例如,以下行定义度量值 cm (厘米)。

[<Measure>] type cm

以下行将度量值 ml (毫升)定义为立方厘米(cm^3)。

[<Measure>] type ml = cm^3

在前面的语法中, 度量 值是涉及单位的公式。 在涉及单位的公式中,整数幂受支持(正和负),单位之间的空格表示两个单位的乘积, * 也表示单位乘积,并 / 指示单位商。 对于倒数单位,可以使用负整数幂或 / 指示单位公式的分子与分母之间的分隔。 分母中的多个单位应用括号括起来。 按空格分隔的 / 单位被解释为分母的一部分,但后面的 * 任何单位被解释为分子的一部分。

可以单独在单元表达式中使用 1 来指示无维度数量,也可以与其他单位(例如在分子中)一起使用。 例如,速率的单位将写入为 1/s,其中 s 指示秒。 单元公式中不使用括号。 未在单位公式中指定数值转换常量;但是,可以使用单元单独定义转换常量,并在单元检查的计算中使用它们。

以各种等效方式编写同一事物的单位公式。 因此,编译器将单位公式转换为一致的形式,它将负数转换为对等形式,将单位分组为单个分子和分母,并按分母和分母对单位进行字母排序。

例如,单位公式 kg m s^-2m /s s * kg 两者都 kg m/s^2转换为 。

在浮点表达式中使用度量单位。 将浮点数与关联的度量单位结合使用会增加另一个类型的安全性级别,并有助于避免使用弱类型浮点数时公式中可能发生的单位不匹配错误。 如果编写使用单位的浮点表达式,则表达式中的单位必须匹配。

可以使用尖括号中的单元公式对文本进行批注,如以下示例所示。

1.0<cm>
55.0<miles/hour>

不要在数字和尖括号之间放置空格;但是,可以包括文本后缀,如 f以下示例所示。

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

此类批注将文本的类型从其基元类型(例如 float)更改为维度类型,例如 float<cm> ,在这种情况下, float<miles/hour>或。 指示无维度数量的单位批注 <1> ,其类型等效于没有单元参数的基元类型。

度量单位的类型是浮点或带符号整数类型,以及括号中指示的额外单位批注。 因此,在编写从 g (gram) 到 kg (kgs) 的转换类型时,将按如下所示描述类型。

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

度量单位用于编译时单元检查,但不保留在运行时环境中。 因此,它们不会影响性能。

度量单位可以应用于任何类型,而不仅仅是浮点类型;但是,仅浮点类型、带符号整型类型和小数类型支持维度数量。 因此,仅对基元类型和包含这些基元类型的聚合使用度量单位是有意义的。

下面的示例说明了度量单位的使用。

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

下面的代码示例演示如何从无维度浮点数转换为维度浮点值。 只需将维度应用于 1.0 即可乘以 1.0。 可以将此项抽象化为类似 degreesFahrenheit函数。

此外,将维度值传递给需要无维度浮点数的函数时,必须使用运算符取消单位或强制转换floatfloat。 在此示例中,将按 1.0<degC> 参数 printf 划分,因为 printf 需要无维度数量。

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

以下示例会话显示此代码的输出和输入。

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

支持度量单位的基元类型

以下类型或类型缩写别名支持度量单位注释:

F# 别名 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

例如,可以批注无符号整数,如下所示:

[<Measure>]
type days

let better_age = 3u<days>

F# RFC FS-1091 中记录了向此功能添加无符号整数类型。

预定义度量单位

命名空间中 FSharp.Data.UnitSystems.SI 提供了一个单元库。 它包括其符号形式的 SI 单位(例如m子名称空间中的UnitSymbols计量单位),以及子名称中的UnitNames全名(例如meter计量单位)。

使用泛型单位

可以编写对具有关联度量单位的数据进行作的泛型函数。 为此,请将类型与泛型单元一起指定为类型参数,如以下代码示例所示。

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

使用泛型单元创建集合类型

以下代码演示如何创建包含具有泛型单位的单个浮点值的聚合类型。 这样就可以创建适用于各种单元的单个类型。 此外,泛型单元通过确保具有一组单元的泛型类型与具有不同单元集的同一泛型类型不同,从而保留类型安全性。 此方法的基础是 Measure 可将属性应用于类型参数。

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

运行时单位

度量单位用于静态类型检查。 编译浮点值时,将消除度量单位,以便在运行时丢失单位。 因此,无法实现依赖于在运行时检查单元的功能的任何尝试。 例如,无法实现 ToString 用于打印单元的函数。

转换

若要将具有单位(例如) float<'u>的类型转换为没有单位的类型,可以使用标准转换函数。 例如,可用于 float 转换为 float 没有单位的值,如以下代码所示。

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

若要将无单位值转换为具有单位的值,可以乘以 1 或 1.0 值,该值用相应的单位进行批注。 但是,对于编写互作性层,还有一些显式函数可用于将无单元值转换为具有单位的值。 这些模块位于 FSharp.Core.LanguagePrimitives 模块中。 例如,若要从无float单位转换为 float<cm>FloatWithMeasure,请使用 FloatWithMeasure,如以下代码所示。

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

另请参阅