Referenční buňky (F#)

Odkazové buňky jsou úložná místa, která umožňují vytvořit proměnlivé hodnoty pomocí odkazové sémantiky.

ref expression

Poznámky

Pomocí operátoru ref před hodnotou vytvoříte novou odkazovou buňku, která zapouzdřuje hodnotu.Zdrojovou hodnotu pak můžete změnit, protože je proměnlivá.

Odkazové buňky obsahují skutečnou hodnotu; není to pouze adresa.Při vytvoření odkazové buňky pomocí operátoru ref se vytvoří kopie zdrojové hodnoty ve formě zapouzdřené proměnlivé hodnoty.

Odkazovou buňku lze zrušit pomocí operátoru ! (vykřičník).

Následující příklad kódu znázorňuje deklaraci a použití odkazových buněk.

// Declare a reference. 
let refVar = ref 6

// Change the value referred to by the reference.
refVar := 50

// Dereference by using the ! operator.
printfn "%d" !refVar

Výstupem je 50.

Odkazové buňky jsou instance generického typu záznamu Ref, který je deklarován následujícím způsobem.

type Ref<'a> =
    { mutable contents: 'a }

Typ 'a ref je synonymem pro Ref<'a>.Kompilátor a technologie IntelliSense v integrovaném vývojovém prostředí zobrazují nejprve tento typ a následně zdrojovou definici.

Operátor ref vytvoří novou odkazovou buňku.Následující kód je deklarace operátoru ref.

let ref x = { contents = x }

V následující tabulce jsou uvedeny funkce, které jsou u odkazové buňky dostupné.

Operátor, člen nebo pole

Popis

Typ

Definice

! (operátor zrušení odkazu)

Vrátí zdrojovou hodnotu.

'a ref -> 'a

let (!) r = r.contents

:= (operátor přiřazení)

Změní zdrojovou hodnotu.

'a ref -> 'a -> unit

let (:=) r x = r.contents <- x

ref (operátor)

Zapouzdří hodnotu do nové odkazové buňky.

'a -> 'a ref

let ref x = { contents = x }

Value (vlastnost)

Získá nebo nastaví zdrojovou hodnotu.

unit -> 'a

member x.Value = x.contents

contents (pole záznamu)

Získá nebo nastaví zdrojovou hodnotu.

'a

let ref x = { contents = x }

Ke zdrojové hodnotě lze získat přístup několika způsoby.Hodnota vrácená operátorem zrušení odkazu (!) není přiřaditelná.Při změně zdrojové hodnoty proto musíte namísto toho použít operátor přiřazení (:=).

Vlastnost Value i pole contents jsou přiřaditelné hodnoty.Můžete je proto použít ke zpřístupnění nebo změně zdrojové hodnoty, jak znázorňuje následující kód.

let xRef : int ref = ref 10

printfn "%d" (xRef.Value)
printfn "%d" (xRef.contents)

xRef.Value <- 11
printfn "%d" (xRef.Value)
xRef.contents <- 12
printfn "%d" (xRef.contents)

Výstup je následující.

10
10
11
12

Pole contents zajišťuje kompatibilitu s jinými verzemi ML a během kompilace zobrazí upozornění.Toto upozornění zakážete parametrem kompilátoru --mlcompatibility.Další informace naleznete v tématu Možnosti kompilátoru (F#).

Příklad

Následující kód znázorňuje použití odkazových buněk při předávání parametrů.Typ Incrementor má metodu Increment, která přebírá parametr obsahující byref v typu parametru.byref v typu parametru označuje, že volající musejí předat odkazovou buňku nebo adresu typické proměnné zadaného typu, v tomto případě int.Zbývající kód znázorňuje, jak volat metodu Increment s oběma těmito typy argumentů a ukazuje, jak pomocí operátoru ref u proměnné vytvořit odkazovou buňku (ref myDelta1).Pak znázorňuje použití operátoru address-of (&) ke generování příslušného argumentu.Nakonec je pomocí odkazové buňky znovu volána metoda Increment, která je deklarována pomocí vazby let.Poslední řádek kódu ukazuje použití operátoru ! ke zrušení odkazu odkazové buňky pro účely tisku.

type Incrementor(delta) =
    member this.Increment(i : int byref) =
        i <- i + delta

let incrementor = new Incrementor(1)
let mutable myDelta1 = 10
incrementor.Increment(ref myDelta1)
// Prints 10:
printfn "%d" myDelta1  

let mutable myDelta2 = 10
incrementor.Increment(&myDelta2)
// Prints 11:
printfn "%d" myDelta2 

let refInt = ref 10
incrementor.Increment(refInt)
// Prints 11:
printfn "%d" !refInt

Další informace o předávání pomocí odkazu naleznete v tématu Parametry a argumenty (F#).

[!POZNÁMKA]

Programátoři v jazyce C# by měli vědět, že ref funguje v jazyku F# jinak než v jazyku C#.Například použití ref při předávání argumentu nemá v jazyku F# stejný účinek jako v jazyku C#.

Odkazové buňky versus proměnlivé proměnné

Odkazové buňky a proměnlivé proměnné lze často použít ve stejných situacích.V některých situacích však proměnlivé proměnné nelze použít a namísto nich je nutné použít odkazovou buňku.Obecně byste měli dát přednost proměnlivým proměnným, pokud je akceptuje kompilátor.Ve výrazech, které generují uzávěry, však kompilátor oznámí, že proměnlivé proměnné nelze použít.Uzávěry jsou lokální funkce generované určitými výrazy jazyka F#, například lambda výrazy, sekvenčními výrazy, výpočetními výrazy a zřetězenými funkcemi, které využívají částečně použité argumenty.Uzávěry generované těmito výrazy se ukládají pro pozdější vyhodnocení.Tento proces není kompatibilní s proměnlivými proměnnými.Proto pokud potřebujete v takovém výrazu proměnlivý stav, musíte použít odkazové buňky.Další informace o uzávěrech naleznete v tématu Uzávěry (jazyk F#).

Následující příklad kódu ukazuje situaci, ve které je nutné použít odkazovou buňku.

// Print all the lines read in from the console. 
let PrintLines1() =
    let mutable finished = false 
    while not finished do 
        match System.Console.ReadLine() with
        | null -> finished <- true
        | s -> printfn "line is: %s" s


// Attempt to wrap the printing loop into a  
// sequence expression to delay the computation. 
let PrintLines2() =
    seq {
        let mutable finished = false 
        // Compiler error: 
        while not finished do   
            match System.Console.ReadLine() with
            | null -> finished <- true
            | s -> yield s
    }

// You must use a reference cell instead. 
let PrintLines3() =
    seq {
        let finished = ref false 
        while not !finished do 
            match System.Console.ReadLine() with
            | null -> finished := true
            | s -> yield s
    }

Odkazová buňka finished v předchozím kódu je zahrnuta v lokálním stavu, což znamená, že proměnné, které jsou v tomto uzávěru, jsou vytvořeny a použity pouze v rámci tohoto výrazu, v tomto případě sekvenčního výrazu.Uvažujte, co se stane, pokud proměnné nejsou lokální.Uzávěry mají přístup také k nelokálnímu stavu, ale v tomto případě se proměnné zkopírují a uloží pomocí hodnoty.Tento proces se označuje jako hodnotová sémantika.To znamená, že se uloží hodnoty v okamžiku zkopírování a žádné následné změny proměnných se nepromítnou.Chcete-li sledovat změny nelokálních proměnných, nebo jinými slovy potřebujete uzávěru, která provádí interakci s nelokálním stavem pomocí referenční sémantiky, musíte použít odkazovou buňku.

Následující příklady kódu znázorňují použití odkazových buněk v uzávěrech.V tomto případě uzávěr vznikne kvůli částečnému použití argumentů funkce.

// The following code demonstrates the use of reference 
// cells to enable partially applied arguments to be changed 
// by later code. 

let increment1 delta number = number + delta

let mutable myMutableIncrement = 10

// Closures created by partial application and literals. 
let incrementBy1 = increment1 1
let incrementBy2 = increment1 2

// Partial application of one argument from a mutable variable. 
let incrementMutable = increment1 myMutableIncrement

myMutableIncrement <- 12

// This line prints 110.
printfn "%d" (incrementMutable 100)

let myRefIncrement = ref 10

// Partial application of one argument, dereferenced 
// from a reference cell. 
let incrementRef = increment1 !myRefIncrement

myRefIncrement := 12

// This line also prints 110.
printfn "%d" (incrementRef 100)

// Reset the value of the reference cell.
myRefIncrement := 10

// New increment function takes a reference cell. 
let increment2 delta number = number + !delta

// Partial application of one argument, passing a reference cell 
// without dereferencing first. 
let incrementRef2 = increment2 myRefIncrement

myRefIncrement := 12

// This line prints 112.
printfn "%d" (incrementRef2 100)

Viz také

Referenční dokumentace

Referenční dokumentace symbolů a operátorů (F#)

Koncepty

Parametry a argumenty (F#)

Další zdroje

Referenční dokumentace jazyka F#