F#
Единицы измерения
Поиск…
замечания
Единицы во время выполнения
Единицы измерения используются только для статической проверки компилятором и не доступны во время выполнения. Они не могут использоваться в отражении или в методах, подобных ToString
.
Например, C # дает double
без единиц для поля типа float<m>
определенного и отображаемого из библиотеки F #.
Обеспечение согласованных единиц в расчетах
Единицы измерения - это дополнительные аннотации типа, которые могут быть добавлены к поплавкам или целым числам. Они могут использоваться для проверки во время компиляции, когда расчеты используют единицы последовательно.
Чтобы определить аннотации:
[<Measure>] type m // meters
[<Measure>] type s // seconds
[<Measure>] type accel = m/s^2 // acceleration defined as meters per second squared
После определения аннотации могут использоваться для проверки того, что выражение приводит к ожидаемому типу.
// Compile-time checking that this function will return meters, since (m/s^2) * (s^2) -> m
// Therefore we know units were used properly in the calculation.
let freeFallDistance (time:float<s>) : float<m> =
0.5 * 9.8<accel> * (time*time)
// It is also made explicit at the call site, so we know that the parameter passed should be in seconds
let dist:float<m> = freeFallDistance 3.0<s>
printfn "%f" dist
Конверсии между единицами
[<Measure>] type m // meters
[<Measure>] type cm // centimeters
// Conversion factor
let cmInM = 100<cm/m>
let distanceInM = 1<m>
let distanceInCM = distanceInM * cmInM // 100<cm>
// Conversion function
let cmToM (x : int<cm>) = x / 100<cm/m>
let mToCm (x : int<m>) = x * 100<cm/m>
cmToM 100<cm> // 1<m>
mToCm 1<m> // 100<cm>
Обратите внимание, что компилятор F # не знает, что 1<m>
равно 100<cm>
. Что касается этого, единицы являются отдельными типами. Вы можете написать аналогичные функции для преобразования от метров до килограммов, и компилятору все равно.
[<Measure>] type kg
// Valid code, invalid physics
let kgToM x = x / 100<kg/m>
Невозможно определить единицы измерения как кратность других единиц, таких как
// Invalid code
[<Measure>] type m = 100<cm>
Однако для определения единиц «за что-то», например, Герца, измеряя частоту, просто «в секунду», довольно просто.
// Valid code
[<Measure>] type s
[<Measure>] type Hz = /s
1 / 1<s> = 1 <Hz> // Evaluates to true
[<Measure>] type N = kg m/s // Newtons, measuring force. Note lack of multiplication sign.
// Usage
let mass = 1<kg>
let distance = 1<m>
let time = 1<s>
let force = mass * distance / time // Evaluates to 1<kg m/s>
force = 1<N> // Evaluates to true
Использование LanguagePrimitives для сохранения или установки единиц
Когда функция не сохраняет единицы автоматически из-за операций более низкого уровня, модуль LanguagePrimitives
может использоваться для установки единиц на примитивах, которые их поддерживают:
/// This cast preserves units, while changing the underlying type
let inline castDoubleToSingle (x : float<'u>) : float32<'u> =
LanguagePrimitives.Float32WithMeasure (float32 x)
Чтобы присвоить единицы измерения значению с плавающей запятой с двойной точностью, просто умножьте их на единицы с правильными единицами:
[<Measure>]
type USD
let toMoneyImprecise (amount : float) =
amount * 1.<USD>
Чтобы присвоить единицы измерения единичному значению, которое не является System.Double, например, поступая из библиотеки, написанной на другом языке, используйте преобразование:
open LanguagePrimitives
let toMoney amount =
amount |> DecimalWithMeasure<'u>
Ниже приведены типы функций, сообщенные F # interactive:
val toMoney : amount:decimal -> decimal<'u>
val toMoneyImprecise : amount:float -> float<USD>
Параметры типа единицы измерения
Атрибут [<Measure>]
может использоваться для параметров типа для объявления типов, которые являются общими для единиц измерения:
type CylinderSize<[<Measure>] 'u> =
{ Radius : float<'u>
Height : float<'u> }
Использование теста:
open Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols
/// This has type CylinderSize<m>.
let testCylinder =
{ Radius = 14.<m>
Height = 1.<m> }
Используйте стандартные типы устройств для обеспечения совместимости
Например, типы для единиц СИ были стандартизованы в основной библиотеке F # в Microsoft.FSharp.Data.UnitSystems.SI
. Чтобы использовать их, откройте соответствующее пространство UnitNames
, UnitNames
или UnitSymbols
. Или, если требуется только несколько единиц СИ, их можно импортировать с помощью псевдонимов типов:
/// Seconds, the SI unit of time. Type abbreviation for the Microsoft standardized type.
type [<Measure>] s = Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols.s
Некоторые пользователи имеют тенденцию делать следующее, что не должно выполняться всякий раз, когда определение уже доступно:
/// Seconds, the SI unit of time
type [<Measure>] s // DO NOT DO THIS! THIS IS AN EXAMPLE TO EXPLAIN A PROBLEM.
Разница становится очевидной при взаимодействии с другим кодом, который относится к стандартным типам SI. Код, который относится к стандартным единицам, совместим, а код, который определяет его собственный тип, несовместим с любым кодом, не использующим его конкретное определение.
Поэтому всегда используйте стандартные типы для единиц СИ. Не имеет значения, ссылаетесь ли вы на UnitNames
или UnitSymbols
, поскольку эквивалентные имена в этих двух относятся к одному типу:
open Microsoft.FSharp.Data.UnitSystems.SI
/// This is valid, since both versions refer to the same authoritative type.
let validSubtraction = 1.<UnitSymbols.s> - 0.5<UnitNames.second>