Ricerca…


Osservazioni

Unità in fase di esecuzione

Le unità di misura vengono utilizzate solo per il controllo statico da parte del compilatore e non sono disponibili in fase di esecuzione. Non possono essere utilizzati in riflessione o in metodi come ToString .

Ad esempio, C # fornisce un double senza unità per un campo di tipo float<m> definito da ed esposto da una libreria F #.

Garantire le unità coerenti nei calcoli

Le unità di misura sono ulteriori annotazioni di tipo che possono essere aggiunte a float o interi. Possono essere utilizzati per verificare in fase di compilazione che i calcoli utilizzano le unità in modo coerente.

Per definire annotazioni:

[<Measure>] type m // meters
[<Measure>] type s // seconds
[<Measure>] type accel = m/s^2 // acceleration defined as meters per second squared

Una volta definite, è possibile utilizzare annotazioni per verificare che un'espressione produca il tipo previsto.

// 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

Conversioni tra unità

[<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>

Nota che il compilatore F # non sa che 1<m> uguale a 100<cm> . Per quanto a lui importa, le unità sono tipi separati. Puoi scrivere funzioni simili per convertire da metri a chilogrammi, e il compilatore non si preoccuperebbe.

[<Measure>] type kg

// Valid code, invalid physics
let kgToM x = x / 100<kg/m>

Non è possibile definire unità di misura come multipli di altre unità come

// Invalid code
[<Measure>] type m = 100<cm>

Tuttavia, definire le unità "per qualcosa", per esempio Hertz, misurare la frequenza, è semplicemente "al secondo", è piuttosto semplice.

// 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

Utilizzare LanguagePrimitives per conservare o impostare le unità

Quando una funzione non conserva automaticamente le unità a causa delle operazioni di livello inferiore, il modulo LanguagePrimitives può essere utilizzato per impostare le unità sulle primitive che le supportano:

/// This cast preserves units, while changing the underlying type
let inline castDoubleToSingle (x : float<'u>) : float32<'u> =
    LanguagePrimitives.Float32WithMeasure (float32 x)

Per assegnare unità di misura a un valore in virgola mobile a precisione doppia, basta moltiplicare per uno con le unità corrette:

[<Measure>]
type USD

let toMoneyImprecise (amount : float) =
   amount * 1.<USD>

Per assegnare unità di misura ad un valore senza unità che non è System.Double, ad esempio, arrivando da una libreria scritta in un'altra lingua, utilizzare una conversione:

open LanguagePrimitives

let toMoney amount =
   amount |> DecimalWithMeasure<'u>

Ecco i tipi di funzione riportati da F # interattivo:

val toMoney : amount:decimal -> decimal<'u>
val toMoneyImprecise : amount:float -> float<USD>

Parametri di tipo unità di misura

L'attributo [<Measure>] può essere utilizzato sui parametri del tipo per dichiarare tipi generici rispetto alle unità di misura:

type CylinderSize<[<Measure>] 'u> =
    { Radius : float<'u>
      Height : float<'u> }

Utilizzo del test:

open Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols

/// This has type CylinderSize<m>.
let testCylinder =
    { Radius = 14.<m>
      Height =  1.<m> }

Utilizzare tipi di unità standardizzati per mantenere la compatibilità

Ad esempio, i tipi per unità SI sono stati standardizzati nella libreria di base F #, in Microsoft.FSharp.Data.UnitSystems.SI . Aprire lo spazio dei nomi secondario appropriato, UnitNames o UnitSymbols , per utilizzarli. Oppure, se sono richieste solo poche unità SI, possono essere importate con alias di tipo:

/// Seconds, the SI unit of time. Type abbreviation for the Microsoft standardized type.
type [<Measure>] s = Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols.s

Alcuni utenti tendono a fare quanto segue, cosa che non dovrebbe essere fatta ogni volta che una definizione è già disponibile:

/// Seconds, the SI unit of time
type [<Measure>] s // DO NOT DO THIS! THIS IS AN EXAMPLE TO EXPLAIN A PROBLEM.

La differenza diventa evidente quando si interfaccia con un altro codice che fa riferimento ai tipi SI standard. Il codice che fa riferimento alle unità standard è compatibile, mentre il codice che definisce il proprio tipo è incompatibile con qualsiasi codice che non utilizza la sua specifica definizione.

Pertanto, utilizzare sempre i tipi standard per le unità SI. Non importa se ti riferisci a UnitNames o UnitSymbols , poiché i nomi equivalenti all'interno di questi due si riferiscono allo stesso tipo:

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>


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow