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

Значения с плавающей запятой и со знаком в 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^-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> единицы указывают безмерное количество, а его тип эквивалентен примитивному типу без параметра единицы.

Тип единицы измерения — это плавающая точка или подписанный целочисленный тип вместе с дополнительной заметкой единицы, указанной в скобках. Таким образом, при записи типа преобразования из 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> то, что printfprintf ожидается безмерное количество.

[<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 подназовом пространстве, так и в полном имени (например 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 . Например, чтобы преобразовать из единицы без единицы floatfloat<cm>в , используйте FloatWithMeasure, как показано в следующем коде.

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

См. также