Единицы измерения

Целочисленные значения с плавающей запятой и со знаком в F # могут иметь связанные единицы измерения, которые обычно используются для обозначения длины, объема, массы и т. д. Используя количественные единицы, вы включаете компилятор, чтобы убедиться, что арифметические связи имеют правильные единицы, что помогает предотвратить ошибки программирования.

Синтаксис

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

Remarks

В предыдущем синтаксисе параметр Unit-Name определяется как единица измерения. Необязательная часть используется для определения новой меры с точки зрения ранее определенных единиц. Например, в следующей строке определяется мера cm (сантиметр).

[<Measure>] type cm

Следующая строка определяет меру ml (миллилитер) как кубический сантиметр ( cm^3 ).

[<Measure>] type ml = cm^3

В предыдущем синтаксисе мера — это формула, в которой участвуют единицы. В формулах, в которых задействованы единицы, целочисленные степени поддерживаются (положительные и отрицательные), пробелы между единицами указывают на произведение двух единиц, * также указывают на произведение единиц и / обозначает частное число единиц. Для обратной единицы можно использовать отрицательное целое число или значение / , которое обозначает разделение между числителем и знаменателем формулы единицы. Несколько единиц в знаменателе должны быть заключены в круглые скобки. Единицы, разделенные пробелами после, обрабатываются / как часть знаменателя, но все единицы после a обрабатываются * как часть числителя.

Можно использовать 1 в выражениях единиц измерения, чтобы указать количество без измерения или вместе с другими единицами, например в числителе. Например, единицы курса записываются как 1/s , где — s секунды. Круглые скобки не используются в формулах единиц. Константы числового преобразования не указываются в формулах единиц; Однако константы преобразования можно определить с помощью единиц по отдельности и использовать их в вычислениях с проверкой единиц измерения.

Формулы единиц, означающие то же самое, можно написать различными способами. Таким образом, компилятор преобразует формулы единиц в согласованную форму, которая преобразует отрицательные степени в обратные, группирует единицы в один числитель и знаменатель и алфабетизес единицы в числителе и знаменателе.

Например, формулы единиц kg m s^-2 и m /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> указывает на неизмерение количества, и его тип эквивалентен типу-примитиву без параметра Unit.

Тип единицы измерения — это целочисленный тип с плавающей запятой или со знаком, а также дополнительный комментарий к единице, обозначенный в квадратных скобках. Таким образом, при написании типа преобразования из g (грамм) в kg (килограммы) эти типы описываются следующим образом.

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 .

Кроме того, при передаче измеренных значений в функции, которые предполагают безразмерные числа с плавающей запятой, необходимо отказаться от единиц или привести к типу с float помощью float оператора. В этом примере вы делите на 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 пространстве имен. Она включает в себя единицы СИ как в форме символов (например, m для счетчика) в UnitSymbols подпространстве имен, так и в полном имени (например, meter для счетчика) в UnitNames подпространстве имен.

Использование универсальных единиц

Можно создавать универсальные функции, которые работают с данными, имеющими связанную единицу измерения. Для этого нужно указать тип вместе с универсальной единицей в качестве параметра типа, как показано в следующем примере кода.

// 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> , используйте флоатвисмеасуре, как показано в следующем коде.

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

См. также