Share via


測量單位 (F#)

更新:2010 年 5 月

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 即可。 您可以將此抽象化為函式,如 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

F# Power Pack 中的測量單位

F# PowerPack 中有單位庫。 此單位庫包含 SI 單位和物理常數。

請參閱

其他資源

F# 語言參考

變更記錄

日期

記錄

原因

2010 年 5 月

修正轉換一節中的程式碼範例。

內容 Bug 修正。