Udostępnij za pośrednictwem


Wyrażenia obliczeń (F#)

Wyrażenia obliczeń w F# przewidują składni wygodne pisanie obliczenia, które mogą być ustawione w kolejności i łączone za pomocą sterowania przepływem konstrukcje i powiązania.Może służyć do zapewniają wygodny składnia dla monads, funkcjonalnych funkcja programowania używany do zarządzania danych, kontroli i efekty uboczne w funkcjonalności programów.

Wbudowane przepływy pracy

Sekwencja wyrażeń są przykładem wyrażenie obliczeń jak Asynchroniczne przepływy pracy i wyrażenia w kwerendzie.Aby uzyskać więcej informacji, zobacz sekwencji, Asynchroniczne przepływy pracy, i Wyrażenia w kwerendzie.

Niektóre funkcje są wspólnych dla wyrażeń sekwencji i asynchroniczne przepływy pracy i ilustrują podstawowa składnia wyrażenie obliczeń:

builder-name { expression }

Składnia poprzedniej Określa, że danym wyrażeniu wyrażenie obliczeń typu określonego przez builder-name.Wyrażenie obliczeń może być wbudowany przepływu pracy, takich jak seq lub async, lub może być coś definiowania.builder-name Jest identyfikatorem wystąpienia typu specjalnych, znane jako typu konstruktora.Typ konstruktora jest typu klasy, która definiuje specjalnych metod określające sposób fragmenty wyrażenie obliczeń są łączone, oznacza to, że kod, która kontroluje, jak wykonuje wyrażenie.Innym sposobem opisania konstruktora klasy jest umożliwia można dostosować działanie wielu F# konstrukcje, takich jak pętle i powiązań.

W wyrażeniach obliczeń dwóch formularzy dostępnych dla niektórych typowych konstrukcji językowych.Konstrukcje wariant można wywołać przy użyciu!(bang) sufiks na pewne słowa kluczowe, takie jak let!, do!i tak dalej.Formularze te specjalne spowodować pewne funkcje zdefiniowane w klasie konstruktora zastąpienie zwykłych wbudowane zachowanie tych operacji.Formularze te przypominają yield! postaci yield słowa kluczowego, które jest używane w wyrażeniach sekwencji.Aby uzyskać więcej informacji, zobacz sekwencji.

Tworzenie nowego typu wyrażenia obliczeń

Tworzenie konstruktora klasy i definiując niektórych specjalnych metod klasy można zdefiniować właściwości wyrażenia obliczeń.Klasa konstruktora może opcjonalnie zdefiniować metody wymienione w poniższej tabeli.

W poniższej tabeli opisano metody, które mogą być używane w klasie konstruktora przepływu pracy.

Metoda

Typowy podpis(y)

Opis

Bind

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

O let! i do! w wyrażeniach obliczeń.

Delay

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

Otacza wyrażenie obliczeń w funkcji.

Return

'T -> M<'T>

O return w wyrażeniach obliczeń.

ReturnFrom

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

O return! w wyrażeniach obliczeń.

Run

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

M<'T> -> 'T

Wykonuje wyrażenie obliczeń.

Combine

M<'T> * M<'T> -> M<'T>lub

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

Wywoływana dla sekwencji w wyrażeniach obliczeń.

For

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

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

O for...do wyrażeń w wyrażeniach obliczeń.

TryFinally

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

O try...finally wyrażeń w wyrażeniach obliczeń.

TryWith

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

O try...with wyrażeń w wyrażeniach obliczeń.

Using

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

O use wiązania w wyrażeniach obliczeń.

While

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

O while...do wyrażeń w wyrażeniach obliczeń.

Yield

'T -> M<'T>

O yield wyrażeń w wyrażeniach obliczeń.

YieldFrom

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

O yield! wyrażeń w wyrażeniach obliczeń.

Zero

unit -> M<'T>

O nazwie puste else gałęziach if...then wyrażeń w wyrażeniach obliczeń.

Wiele metod w klasie konstruktora Użyj i zwróć M<'T> konstrukcji, która jest zazwyczaj oddzielnie określonym typem, który charakteryzuje rodzaju obliczeniach połączone, na przykład, Async<'T> dla Asynchroniczne przepływy pracy i Seq<'T> dla przepływów pracy z sekwencji.Podpisy tych metod mogły być łączone i zagnieżdżony z innymi, tak, aby obiekt przepływu pracy zwrócony z jednego konstrukcji mogą być przekazywane do następnego.Kompilator po przeanalizowaniu wyrażenie obliczeń konwertuje wyrażenie Seria zagnieżdżonych wywołań funkcji przy użyciu metod opisanych w poprzedniej tabeli i kod w wyrażeniu obliczeń.

Zagnieżdżone wyrażenie ma następującą postać:

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

W kodzie powyżej, wzywa do Run i Delay są pomijane, jeśli nie są zdefiniowane w klasie Konstruktora wyrażeń obliczeń.Jednostka wyrażenie obliczeń, w tym miejscu jest oznaczona jako {| cexpr |}, jest tłumaczona na wywołania, obejmujące metody klasy konstruktora przez tłumaczenia, opisane w poniższej tabeli.Wyrażenie obliczeń {| cexpr |} jest rekursywnie zdefiniowanych zgodnie z tymi tłumaczeniami gdzie expr jest wyrażeniem F# i cexpr to wyrażenie obliczeń.

Wyrażenie

Tłumaczenie

{| 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()

W poprzedniej tabeli other-expr opisuje wyrażenie, które w przeciwnym razie nie jest wymienione w tabeli.Klasy konstruktora nie trzeba zaimplementować wszystkie metody i obsługują wszystkie tłumaczenia, wymienione w poprzedniej tabeli.Te konstrukcje, które nie są implementowane nie są dostępne w wyrażeniach obliczeń tego typu.Na przykład, jeśli nie chcesz obsługiwać use słowa kluczowego w wyrażeniach obliczeń, można pominąć definicji Use w klasie konstruktora.

Poniższy przykład kodu pokazuje wyrażenie obliczeń, która hermetyzuje obliczeń jak serii kroków, które można ocenić jeden krok w czasie.A izolowanie Unii typu OkOrException, koduje stanie błędu wyrażenia jak dotąd ocenione.Ten kod ilustruje kilka typowych wzorców, które można używać w wyrażeniach obliczeń, takich jak implementacje dodatkową niektórych metod konstruktora.

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

Wyrażenie obliczeń ma typ podstawowy, który zwraca wartość wyrażenia.Typ podstawowy może reprezentować obliczony wynik lub opóźnione obliczeń, które mogą być wykonywane, lub może zapewnić sposób do iteracji typów kolekcji.W poprzednim przykładzie typ podstawowy został Eventually.Wyrażenie sekwencji typ podstawowy jest IEnumerable<T>.W wyrażeniu kwerendy jest podstawowy typ IQueryable<T>.Przepływ pracy asychronous, typ podstawowy jest Async.Async Obiekt reprezentuje pracy do wykonania do obliczenia wyniku.Na przykład wywołanie Async.RunSynchronously do wykonywania obliczeń i zwraca wynik.

Operacje niestandardowe

Można definiować niestandardowe operacji na wyrażenie obliczeń i używać niestandardowych operacji jako podmiot wyrażenie obliczeń.Na przykład można dołączyć operator kwerendy w wyrażeniu kwerendy.Podczas definiowania niestandardowych operacji należy zdefiniować wydajność i metod w wyrażeniu obliczeń.Aby zdefiniować niestandardowy operacji, umieścić ją w klasie konstruktora dla wyrażenia obliczeń, a następnie Zastosuj CustomOperationAttribute.Ten atrybut przyjmuje ciąg jako argument Nazwa ma być używany w niestandardowych operacji jest.Nazwa ta wchodzi w zakres od początku otwarcia klamrowy wyrażenia obliczeń.Dlatego nie należy używać identyfikatorów, które mają taką samą nazwę jak niestandardowe operacji w tym bloku.Na przykład, takie jak uniknąć użycia identyfikatorów all lub last w wyrażeniach kwerend.

Zobacz też

Informacje

Asynchroniczne przepływy pracy (F#)

sekwencji (F#)

wyrażenia w kwerendzie (F#)

Inne zasoby

F# Language Reference