Sdílet prostřednictvím


Výpočetní výrazy (F#)

Výpočet výrazy v F# poskytují pohodlný syntaxe zápisu výpočty, které mohou být seřazeny a kombinované použití řízení toku konstrukce a vazby.Lze použít poskytovat pohodlný syntaxe monads, funkční programovací funkce, která slouží ke správě dat, řízení a vedlejší účinky funkčnosti aplikací.

Předdefinované pracovní postupy

Řada výrazů jsou příklad výpočtu výrazu jsou asynchronní pracovní postupy a výrazech dotazu.Další informace naleznete v sekvence, Asynchronní pracovní postupy, a Výrazy dotazu.

Některé funkce jsou společné pro sekvence výrazů a asynchronní pracovní postupy a znázorňují základní syntaxe pro výpočet výrazu:

builder-name { expression }

Předchozí syntaxe Určuje, že daný výraz je výpočet výrazu určeného typu builder-name.Předdefinovaný pracovní postup může být například výpočtu výrazu seq nebo async, nebo může být něco definujete.builder-name Je identifikátor instance známé jako zvláštní typ typu Tvůrce.Tvůrce typ je typ třídy, která definuje speciální metody, které řídí způsob fragmenty výpočet výrazu jsou kombinovány, je kód která řídí, jak výraz provede.Dalším způsobem popisují Tvůrce třídy je říci, že umožňuje přizpůsobit fungování mnoho F# konstrukcí, například smyčky a vazby.

Ve výrazech výpočtu dva formuláře jsou k dispozici pro některé běžné konstrukce jazyka.Varianty konstrukce lze vyvolat pomocí!(bang) příponu určitých klíčových slov, jako například let!, do!, atd.Tyto zvláštní formuláře způsobit určité funkce definované ve třídě Tvůrce k nahrazení běžných předdefinované chování těchto operací.Tyto formuláře vypadat yield! forma yield klíčové slovo použité v pořadí výrazů.Další informace naleznete v sekvence.

Vytvoření nového typu výpočtu výrazu

Vytvoření třídy tvůrce a definováním některých zvláštních metod třídy můžete definovat vlastnosti výpočtu výrazech.Třída Tvůrce volitelně definovat metody uvedené v následující tabulce.

Následující tabulka popisuje metody, které lze použít ve třídě Tvůrce pracovního postupu.

Metoda

Typická podpis(y)

Description

Bind

M<'T> * ('T -> M<'U>) -> M<'U>

Pro let! a do! ve výrazech výpočtu.

Delay

(unit -> M<'T>) -> M<'T>

Výpočet výrazu jako funkce zalomí.

Return

'T -> M<'T>

Pro return ve výrazech výpočtu.

ReturnFrom

M<'T> -> M<'T>

Pro return! ve výrazech výpočtu.

Run

M<'T> -> M<'T>nebo

M<'T> -> 'T

Provede výpočet výrazu.

Combine

M<'T> * M<'T> -> M<'T>nebo

M<unit> * M<'T> -> M<'T>

Pro řazení ve výrazech výpočtu volána.

For

seq<'T> * ('T -> M<'U>) -> M<'U>nebo

seq<'T> * ('T -> M<'U>) -> seq<M<'U>>

Pro for...do výrazy ve výpočtu výrazy.

TryFinally

M<'T> * (unit -> unit) -> M<'T>

Pro try...finally výrazy ve výpočtu výrazy.

TryWith

M<'T> * (exn -> M<'T>) -> M<'T>

Pro try...with výrazy ve výpočtu výrazy.

Using

'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable

Pro use vazby ve výrazech výpočtu.

While

(unit -> bool) * M<'T> -> M<'T>

Pro while...do výrazy ve výpočtu výrazy.

Yield

'T -> M<'T>

Pro yield výrazy ve výpočtu výrazy.

YieldFrom

M<'T> -> M<'T>

Pro yield! výrazy ve výpočtu výrazy.

Zero

unit -> M<'T>

Pro prázdné else pobočkám z if...then výrazy ve výpočtu výrazy.

Mnohé metody třídy Tvůrce použít a vraťte M<'T> konstrukci, která je obvykle samostatně definovaný typ, který charakterizuje druh výpočty kombinovat, například Async<'T> pro asynchronní pracovní postupy a Seq<'T> pro pracovní postupy sekvence.Podpisy těchto metod je společně a vzájemně vnořené povolit tak, aby vrátil jeden konstrukce objektu pracovního postupu mohou být předány na další.Kompilátor při zpracování výpočtu výraz, výraz převede na řadu vnořených volání funkcí pomocí metody uvedené v předchozí tabulce a kód výpočtu výrazu.

Vnořený výraz je v následujícím formátu:

builder.Run(builder.Delay(fun () -> {| cexpr |}))

Výše uvedený kód volání na Run a Delay jsou vynechány, pokud nejsou definovány ve třídě Tvůrce výrazů výpočtu.Subjekt výpočet výrazu, zde označený jako {| cexpr |}, převedena do volání metody třídy Tvůrce zahrnující překlady, které jsou popsány v následující tabulce.Výraz pro výpočet {| cexpr |} je definovaný rekurzivně podle těchto překladů kde expr je výraz F# a cexpr je výraz pro výpočet.

Výraz

Překlad

{| let binding in cexpr |}

let binding in {| cexpr |}

{| let! pattern = expr in cexpr |}

builder.Bind(expr, (fun pattern -> {| cexpr |}))

{| do! expr in cexpr |}

builder.Bind(expr1, (fun () -> {| cexpr |}))

{| yield expr |}

builder.Yield(expr)

{| yield! expr |}

builder.YieldFrom(expr)

{| return expr |}

builder.Return(expr)

{| return! expr |}

builder.ReturnFrom(expr)

{| use pattern = expr in cexpr |}

builder.Using(expr, (fun pattern -> {| cexpr |}))

{| use! value = expr in cexpr |}

builder.Bind(expr, (fun value -> builder.Using(value, (fun value -> {| cexpr |}))))

{| if expr then cexpr0 |}

if expr then {| cexpr0 |} else binder.Zero()

{| if expr then cexpr0 else cexpr1 |}

if expr then {| cexpr0 |} else {| cexpr1 |}

{| match expr with | pattern_i -> cexpr_i |}

match expr with | pattern_i -> {| cexpr_i |}

{| for pattern in expr do cexpr |}

builder.For(enumeration, (fun pattern -> {| cexpr }|))

{| for identifier = expr1 to expr2 do cexpr |}

builder.For(enumeration, (fun identifier -> {| cexpr }|))

{| while expr do cexpr |}

builder.While(fun () -> expr), builder.Delay({|cexpr |})

{| try cexpr with | pattern_i -> expr_i |}

builder.TryWith(builder.Delay({| cexpr |}), (fun value -> match value with | pattern_i -> expr_i | exn -> reraise exn)))

{| try cexpr finally expr |}

builder.TryFinally(builder.Delay( {| cexpr |}), (fun () -> expr))

{| cexpr1; cexpr2 |}

builder.Combine({|cexpr1 |}, {| cexpr2 |})

{| other-expr; cexpr |}

expr; {| cexpr |}

{| other-expr |}

expr; builder.Zero()

V předchozí tabulce other-expr popisuje výraz, který jinak není uveden v tabulce.Třída Tvůrce nemusí provádět všechny metody a podporují všechny překlady uvedené v předchozí tabulce.Tyto konstrukce, které nejsou implementovány nejsou k dispozici ve výrazech výpočtu tohoto typu.Například pokud chcete podporovat use klíčové slovo ve výrazech výpočtu, můžete vynechat definice Use ve své třídě Tvůrce.

Následující příklad kódu ukazuje výpočet výrazu, který zapouzdří výpočtu jako řadu kroků, které mohou být hodnoceny vždy jeden krok.Unie typu discriminated a OkOrException, jako vyhodnotit dosavadní kóduje stav chyby výrazu.Tento kód ukazuje několik typické vzorky, které lze použít ve výpočtu výrazech, například často implementace některých metod Tvůrce.

// Computations that can be run step by step
type Eventually<'T> =
    | Done of 'T
    | NotYetDone of (unit -> Eventually<'T>)

module Eventually =
    // The bind for the computations. Append 'func' to the
    // computation.
    let rec bind func expr =
        match expr with
        | Done value -> NotYetDone (fun () -> func value)
        | NotYetDone work -> NotYetDone (fun () -> bind func (work()))

    // Return the final value wrapped in the Eventually type.
    let result value = Done value

    type OkOrException<'T> =
    | Ok of 'T
    | Exception of System.Exception

    // The catch for the computations. Stitch try/with throughout
    // the computation, and return the overall result as an OkOrException.
    let rec catch expr =
        match expr with
        | Done value -> result (Ok value)
        | NotYetDone work ->
            NotYetDone (fun () ->
            let res = try Ok(work()) with | exn -> Exception exn
            match res with
            | Ok cont -> catch cont // note, a tailcall
            | Exception exn -> result (Exception exn))

    // The delay operator.
    let delay func = NotYetDone (fun () -> func())

    // The stepping action for the computations.
    let step expr =
        match expr with
        | Done _ -> expr
        | NotYetDone func -> func ()

    // The rest of the operations are boilerplate.
    // The tryFinally operator.
    // This is boilerplate in terms of "result", "catch", and "bind".
    let tryFinally expr compensation =
        catch (expr)
        |> bind (fun res -> compensation();
                            match res with
                            | Ok value -> result value
                            | Exception exn -> raise exn)

    // The tryWith operator.
    // This is boilerplate in terms of "result", "catch", and "bind".
    let tryWith exn handler =
        catch exn
        |> bind (function Ok value -> result value | Exception exn -> handler exn)

    // The whileLoop operator.
    // This is boilerplate in terms of "result" and "bind".
    let rec whileLoop pred body =
        if pred() then body |> bind (fun _ -> whileLoop pred body)
        else result ()

    // The sequential composition operator.
    // This is boilerplate in terms of "result" and "bind".
    let combine expr1 expr2 =
        expr1 |> bind (fun () -> expr2)
    
    // The using operator.
    let using (resource: #System.IDisposable) func =
        tryFinally (func resource) (fun () -> resource.Dispose())

    // The forLoop operator.
    // This is boilerplate in terms of "catch", "result", and "bind".
    let forLoop (collection:seq<_>) func =
        let ie = collection.GetEnumerator()
        tryFinally (whileLoop (fun () -> ie.MoveNext())
                     (delay (fun () -> let value = ie.Current in func value)))
                     (fun () -> ie.Dispose())

// The builder class.
type EventuallyBuilder() =
    member x.Bind(comp, func) = Eventually.bind func comp
    member x.Return(value) = Eventually.result value
    member x.ReturnFrom(value) = value
    member x.Combine(expr1, expr2) = Eventually.combine expr1 expr2
    member x.Delay(func) = Eventually.delay func
    member x.Zero() = Eventually.result ()
    member x.TryWith(expr, handler) = Eventually.tryWith expr handler
    member x.TryFinally(expr, compensation) = Eventually.tryFinally expr compensation
    member x.For(coll:seq<_>, func) = Eventually.forLoop coll func
    member x.Using(resource, expr) = Eventually.using resource expr

let eventually = new EventuallyBuilder()
    
let comp =
    eventually { for x in 1 .. 2 do
                    printfn " x = %d" x
                 return 3 + 4 }

// Try the remaining lines in F# interactive to see how this 
// computation expression works in practice.
let step x = Eventually.step x


// returns "NotYetDone <closure>"
comp |> step

// prints "x = 1"
// returns "NotYetDone <closure>"
comp |> step |> step


// prints "x = 1"
// prints "x = 2"
// returns "NotYetDone <closure>"
comp |> step |> step |> step |> step |> step |> step



// prints "x = 1"
// prints "x = 2"
// returns "Done 7"
comp |> step |> step |> step |> step |> step |> step |> step |> step

Výpočet výrazu má základní typ, který vrátí výraz.Základní typ může představovat vypočítaný výsledek nebo opožděné výpočtu, který může být provedena nebo ji mohou stanovit způsob, jak iterovat nějaký typ kolekce.V předchozím příkladu základní typ byl Eventually.Výraz sekvence pro základní typ je IEnumerable.Pro výrazu dotazu je základní typ IQueryable.Pracovním asychronous základní typ je asynchronní.Async Objekt představuje práce provedena vypočítat výsledek.Například při volání Async.RunSynchronously k provedení výpočtu a vrácení výsledků.

Vlastní operace

Můžete definovat vlastní operace na výpočet výrazu a použít vlastní operace jako operátor ve výrazu výpočtu.Operátor dotazu můžete například zahrnout ve výrazu dotazu.Při definování vlastní operace je nutné definovat výnos a metody výpočtu výrazu.Chcete-li definovat vlastní operaci put Tvůrce třídy pro výpočet výraz a pak použít CustomOperationAttribute.Tento atribut má řetězec jako argument, který je pro vlastní činnost.Tento název se stává na začátku otevírací složená závorka výpočet výrazu oboru.Proto není vhodné používat identifikátorů, které mají stejný název jako vlastní operace v tomto bloku.Například vyhnout použití identifikátorů jako all nebo last ve výrazech dotazu.

Viz také

Referenční dokumentace

Asynchronní pracovní postupy (F#)

sekvencí (F#)

výrazy dotazu (F#)

Další zdroje

Referenční dokumentace jazyka F#