Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
Az F# két fő funkcióterülettel rendelkezik, amelyek az alacsony szintű programozás területén foglalkoznak:
- Azok a
byref//inrefoutreftípusok, amelyek felügyelt mutatók. A használat korlátozásokkal rendelkezik, így futásidőben nem állítható össze érvénytelen program. - A
byref-like struct, amely egy olyan szerkezet , amely hasonló szemantikával és ugyanolyan fordítási időkorlátokkal rendelkezik, mintbyref<'T>a . Ilyen például a .Span<T>
Szemantika
// Byref types as parameters
let f (x: byref<'T>) = ()
let g (x: inref<'T>) = ()
let h (x: outref<'T>) = ()
// Calling a function with a byref parameter
let mutable x = 3
f &x
// Declaring a byref-like struct
open System.Runtime.CompilerServices
[<Struct; IsByRefLike>]
type S(count1: int, count2: int) =
member x.Count1 = count1
member x.Count2 = count2
Byref, inref és outref
A következők három formája byreflétezik:
-
inref<'T>, egy felügyelt mutató az alapul szolgáló érték olvasásához. -
outref<'T>, egy felügyelt mutató a mögöttes értékre való íráshoz. -
byref<'T>, egy felügyelt mutató a mögöttes érték olvasásához és írásához.
A byref<'T> továbbítható, ahol egy inref<'T> várható. Hasonlóképpen, átadható egy byref<'T> olyan érték, ahol a outref<'T> várt érték szerepel.
Byrefs használata
Ha egy inref<'T>mutatót szeretne használni, a következővel kell egy mutatóértéket &lekérnie:
open System
let f (dt: inref<DateTime>) =
printfn $"Now: %O{dt}"
let usage =
let dt = DateTime.Now
f &dt // Pass a pointer to 'dt'
Ha egy vagy byref<'T>több outref<'T> elemet használva szeretne írni a mutatóra, akkor azt az értéket is meg kell adnia, amelybe mutablemutatót fog.
open System
let f (dt: byref<DateTime>) =
printfn $"Now: %O{dt}"
dt <- DateTime.Now
// Make 'dt' mutable
let mutable dt = DateTime.Now
// Now you can pass the pointer to 'dt'
f &dt
Ha csak az egérmutatót írja ahelyett, hogy elolvassa, fontolja meg a helyett a byref<'T>használatátoutref<'T>.
Inref szemantikák
Vegye figyelembe a következő kódot:
let f (x: inref<SomeStruct>) = x.SomeField
Szemantikailag ez a következőket jelenti:
- A mutató tulajdonosa
xcsak az érték olvasására használhatja. - A beágyazott mezőkre
structbeolvasottSomeStructegérmutatók típustinref<_>kapnak.
A következő is igaz:
- Nincs jelentősége annak, hogy más szálak vagy aliasok nem rendelkeznek írási hozzáféréssel
x. - Nincs olyan hatása, hogy
SomeStructnem módosítható, mert az,xhogy egyinref.
A nem módosítható F#-értéktípusok esetében azonban a this mutató inrefegy .
Ezek a szabályok együttesen azt jelentik, hogy a mutató tulajdonosa inref nem módosíthatja a mutatott memória közvetlen tartalmát.
Outref szemantikák
A cél outref<'T> az, hogy jelezze, hogy a mutatót csak erre kell írni. Váratlanul lehetővé teszi a outref<'T> mögöttes érték olvasását a neve ellenére. Ez kompatibilitási célokat szolgál.
Szemantikailag nem más, outref<'T> mint byref<'T>egy különbség: a paraméterekkel rendelkező outref<'T> metódusok implicit módon egy tuple visszatérési típusba vannak felépítve, ugyanúgy, mint amikor egy metódust paraméterrel [<Out>] hívnak meg.
type C =
static member M1(x, y: _ outref) =
y <- x
true
match C.M1 1 with
| true, 1 -> printfn "Expected" // Fine with outref, error with byref
| _ -> printfn "Never matched"
Interop with C#
A C# a visszatérés mellett a kulcsszavakat és out ref a in ref kulcsszavakat ref is támogatja. Az alábbi táblázat bemutatja, hogyan értelmezi az F# a C# által kibocsátott adatokat:
| C#-szerkezet | F#-következtetések |
|---|---|
ref visszatérési érték |
outref<'T> |
ref readonly visszatérési érték |
inref<'T> |
in ref paraméter |
inref<'T> |
out ref paraméter |
outref<'T> |
Az alábbi táblázat azt mutatja be, hogy az F# mit bocsát ki:
| F#-szerkezet | Kibocsátott szerkezet |
|---|---|
inref<'T> argumentum |
[In] attribútum az argumentumon |
inref<'T> visszatérés |
modreq attribútum az értéken |
inref<'T> absztrakciós pontban vagy implementációban |
modreq argumentumon vagy visszatérésen |
outref<'T> argumentum |
[Out] attribútum az argumentumon |
Következtetési és túlterhelési szabályok beírása
Az inref<'T> F#-fordító a következő esetekben következtet egy típusra:
- Attribútummal rendelkező
IsReadOnly.NET-paraméter vagy visszatérési típus. - Egy
thisolyan szerkezettípus mutatója, amely nem tartalmaz módosítható mezőket. - Egy másik
inref<_>mutatóból származtatott memóriahely címe.
Amikor egy implicit címet inref vesz fel, a típus SomeType argumentumával rendelkező túlterhelést előnyben részesíti a túlterhelés egy típus inref<SomeType>argumentumával. Például:
type C() =
static member M(x: System.DateTime) = x.AddDays(1.0)
static member M(x: inref<System.DateTime>) = x.AddDays(2.0)
static member M2(x: System.DateTime, y: int) = x.AddDays(1.0)
static member M2(x: inref<System.DateTime>, y: int) = x.AddDays(2.0)
let res = System.DateTime.Now
let v = C.M(res)
let v2 = C.M2(res, 4)
Mindkét esetben a túlterhelések a System.DateTime túlterhelések inref<System.DateTime>helyett feloldódnak.
Byref-szerű szerkezetek
A trió mellett byref//inrefoutref saját szerkezeteket is meghatározhat, amelyek megfelelnek a byref-szerű szemantikának. Ez a IsByRefLikeAttribute attribútummal történik:
open System
open System.Runtime.CompilerServices
[<IsByRefLike; Struct>]
type S(count1: Span<int>, count2: Span<int>) =
member x.Count1 = count1
member x.Count2 = count2
IsByRefLike nem azt jelenti , hogy Struct. Mindkettőnek jelen kell lennie a típuson.
Az F# "byref-szerű" szerkezete veremhez kötött értéktípus. A rendszer soha nem foglalja le a felügyelt halomra. A byref-like struct hasznos a nagy teljesítményű programozáshoz, mivel erős ellenőrzésekkel kényszerítik ki az élettartamra és a nem rögzítésre vonatkozóan. A szabályok a következők:
- Használhatók függvényparaméterekként, metódusparaméterekként, helyi változókként, metódusvisszatérésekként.
- Nem lehetnek statikusak vagy példányok tagjai egy osztálynak vagy normál szerkezetnek.
- Nem rögzíthetők lezáró szerkezettel (
asyncmetódusokkal vagy lambdakifejezésekkel). - Ezek nem használhatók általános paraméterként.
- Az F# 9-től kezdődően ez a korlátozás enyhül, ha az általános paraméter c# nyelven van definiálva az újrafstrukturált korlátozásokkal. Az F# a byref-szerű típusok és metódusok esetében képes példányosítani az ilyen általános generikusokat. Ez néhány példaként a felhasználó által biztosított akkumulátorfüggvény
String.string Create<TState>(int length, TState state, SpanAction<char, TState> action)() BCL-delegált típusait (Action<>,Func<>felületeit (IEnumerable<>,IComparable<>) és általános argumentumait érinti. - Az F#-ban nem lehet byref-szerű típusokat támogató általános kódot létrehozni.
- Az F# 9-től kezdődően ez a korlátozás enyhül, ha az általános paraméter c# nyelven van definiálva az újrafstrukturált korlátozásokkal. Az F# a byref-szerű típusok és metódusok esetében képes példányosítani az ilyen általános generikusokat. Ez néhány példaként a felhasználó által biztosított akkumulátorfüggvény
Ez az utolsó pont kulcsfontosságú az F# folyamatstílusú programozáshoz, csakúgy, mint |> egy általános függvény, amely paraméterezi a bemeneti típusokat. Ez a korlátozás a jövőben enyhíthető |> , mivel beágyazott, és nem indít hívásokat a nem beágyazott általános függvényekre a szervezetben.
Bár ezek a szabályok erősen korlátozzák a használatot, azért teszik ezt, hogy biztonságosan teljesítsék a nagy teljesítményű számítástechnika ígéretét.
Byref visszaadja
Az F# függvényekből vagy tagokból származó Byref-visszatérések előállíthatók és felhasználhatók. -returning metódus használata byrefesetén az érték implicit módon hareferens lesz. Például:
let squareAndPrint (data : byref<int>) =
let squared = data*data // data is implicitly dereferenced
printfn $"%d{squared}"
Az érték byref visszaadásához az értéket tartalmazó változónak hosszabb ideig kell élnie, mint az aktuális hatókör.
A byref visszaadásához használja &value is (ahol az érték egy olyan változó, amely hosszabb ideig él, mint az aktuális hatókör).
let mutable sum = 0
let safeSum (bytes: Span<byte>) =
for i in 0 .. bytes.Length - 1 do
sum <- sum + int bytes[i]
&sum // sum lives longer than the scope of this function.
Az implicit hareferencia elkerülése érdekében, például ha több láncolt híváson keresztül továbbít egy hivatkozást, használja &x (hol x van az érték).
A visszatéréshez byrefközvetlenül is hozzárendelhet . Fontolja meg a következő (rendkívül imperatív) programot:
type C() =
let mutable nums = [| 1; 3; 7; 15; 31; 63; 127; 255; 511; 1023 |]
override _.ToString() = String.Join(' ', nums)
member _.FindLargestSmallerThan(target: int) =
let mutable ctr = nums.Length - 1
while ctr > 0 && nums[ctr] >= target do ctr <- ctr - 1
if ctr > 0 then &nums[ctr] else &nums[0]
[<EntryPoint>]
let main argv =
let c = C()
printfn $"Original sequence: %O{c}"
let v = &c.FindLargestSmallerThan 16
v <- v*2 // Directly assign to the byref return
printfn $"New sequence: %O{c}"
0 // return an integer exit code
Ez a kimenet:
Original sequence: 1 3 7 15 31 63 127 255 511 1023
New sequence: 1 3 7 30 31 63 127 255 511 1023
Hatókörkezelés byrefs esetén
A letkötött értékek hivatkozása nem haladhatja meg azt a hatókört, amelyben definiálták. A következők például nem engedélyezettek:
let test2 () =
let x = 12
&x // Error: 'x' exceeds its defined scope!
let test () =
let x =
let y = 1
&y // Error: `y` exceeds its defined scope!
()
Ez megakadályozza, hogy különböző eredményeket kap, attól függően, hogy optimalizálással fordít-e vagy sem.