Поделиться через


Единицы измерения (F#)

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

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

Заметки

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

[<Measure>] type cm

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

[<Measure>] type ml = cm^3

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

В выражениях единиц можно использовать 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 

// 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. В этом примере для аргументов функции printf выполняется деление на 1.0<degC>, так как функция 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 mutable floatValue = 0.
if System.Double.TryParse(input, &floatValue)
   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.

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

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

// 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. Однако для создания уровней взаимодействия также существуют некоторые явные функции, которые можно использовать для преобразования значений без единиц измерения в значения с единицами измерения. Они находятся в модуле Microsoft.FSharp.Core.LanguagePrimitives. Например, для преобразования значения float без единиц измерения в значение float<cm> необходимо использовать функцию FloatWithMeasure, как показано в следующем коде.

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

Единицы измерения в Power Pack языка F#

В состав PowerPack для языка F# входит библиотека единиц измерения. Она включает единицы системы СИ и физические константы.

См. также

Другие ресурсы

Справочник по языку F#

Журнал изменений

Дата

Журнал

Причина

Май 2010

Исправлен пример кода в подразделе "Преобразования".

Исправление ошибки содержимого.