Compartir a través de


Expresiones de cálculo (F#)

Las expresiones de cálculo en F# proporcionan una sintaxis adecuada para escribir cálculos que se pueden secuenciar y combinar mediante enlaces y construcciones de flujo de control. Se pueden utilizar para proporcionar una sintaxis apropiada para los monads, una característica de programación funcional que se puede utilizar para administrar datos, controles y efectos secundarios en programas funcionales.

Flujos de trabajo integrados

Las expresiones de secuencia son un ejemplo de una expresión de cálculo, al igual que los flujos de trabajo asincrónicos. Para obtener más información acerca de las expresiones de secuencia, vea Secuencias. Para obtener más información sobre los flujos de trabajo asincrónicos, vea Flujos de trabajo asincrónicos.

Algunas características son comunes a las expresiones de secuencia y los flujos de trabajo asincrónicos, y muestran la sintaxis básica de una expresión de cálculo:

builder-name { expression }

En la sintaxis anterior, se especifica que esa expresión es una expresión de cálculo del tipo especificado por builder-name. La expresión de cálculo puede ser un flujo de trabajo integrado, como seq o async, o puede ser algo definido por el usuario. builder-name es el identificador de una instancia de un tipo especial denominado tipo de generador. El tipo de generador es un tipo de clase que define los métodos especiales que determinan la forma en que se combinan los fragmentos de la expresión de cálculo; es decir, el código que controla cómo se ejecuta la expresión. Para describir una clase de generador de otra forma, se puede decir que permite personalizar el funcionamiento de muchas construcciones de F#, como bucles y enlaces.

En las expresiones de cálculo, existen dos formas disponibles para algunas construcciones de lenguaje comunes. Las construcciones variantes se pueden invocar usando el sufijo ! (bang) con determinadas palabras clave, como let!, do!, etc. Estas formas especiales dan lugar a que algunas funciones definidas en la clase de generador reemplacen el comportamiento integrado ordinario de estas operaciones. Estas formas se parecen a la forma yield! de la palabra clave yield que se usa en expresiones de secuencia. Para obtener más información, vea Secuencias.

Crear un nuevo tipo de expresión de cálculo

Puede definir las características de sus propias expresiones de cálculo creando una clase de generador y definiendo en ella algunos métodos especiales. De manera opcional, la clase de generador puede definir los métodos que figuran en la siguiente tabla.

En la tabla siguiente se describen los métodos que se pueden utilizar en una clase de generador de flujo de trabajo.

Método

Signaturas típicas

Descripción

Bind

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

Se invoca para let! y do! en las expresiones de cálculo.

Delay

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

Encapsula una expresión de cálculo como una función.

Return

'T -> M<'T>

Se invoca para return en las expresiones de cálculo.

ReturnFrom

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

Se invoca para return! en las expresiones de cálculo.

Run

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

M<'T> -> 'T

Ejecuta una expresión de cálculo.

Combine

M<'T> * M<'T> -> M<'T> o

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

Se le llama para las secuencias en las expresiones de cálculo.

For

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

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

Se invoca para las expresiones for...do en las expresiones de cálculo.

TryFinally

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

Se invoca para las expresiones try...finally en las expresiones de cálculo.

TryWith

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

Se invoca para las expresiones try...with en las expresiones de cálculo.

Using

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

Se invoca para los enlaces use en las expresiones de cálculo.

While

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

Se invoca para las expresiones while...do en las expresiones de cálculo.

Yield

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

Se invoca para las expresiones yield en las expresiones de cálculo.

YieldFrom

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

Se invoca para las expresiones yield! en las expresiones de cálculo.

Zero

unit -> M<'T>

Se invoca para las bifurcaciones else vacías de las expresiones if...then en las expresiones de cálculo.

Muchos de los métodos de una clase de generador de flujo de trabajo usan y devuelven una construcción M<'T>, que suele ser un tipo definido de forma independiente que caracteriza los cálculos que se van a combinar; por ejemplo, Async<'T> para los flujos de trabajo asincrónicos y Seq<'T> para los flujos de trabajo secuenciales. Las signaturas de estos métodos les permiten combinarse y anidarse unos con otros, de modo que el objeto de flujo de trabajo devuelto por una construcción se pueda pasar a la siguiente. Cuando el compilador analiza una expresión de cálculo, la convierte en una serie de llamadas a funciones anidadas utilizando para ello los métodos de la tabla anterior y el código de la expresión de cálculo.

La expresión anidada tiene el formato siguiente:

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

En el código anterior, las llamadas a Run y Delay se omiten si no se definen en la clase de generador de expresiones de cálculo. El cuerpo de la expresión de cálculo, denotado aquí como {| cexpr |}, se convierte en llamadas que involucran a los métodos de la clase de generadores mediante las conversiones que se describen en la tabla siguiente. La expresión de cálculo {| cexpr |} se define de forma recursiva con arreglo a estas conversiones, donde expr es una expresión de F# y cexpr es una expresión de cálculo.

Expresión

Conversión

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

En la tabla anterior, other-expr describe una expresión que, de otra forma, no aparecería en la tabla. Una clase de generadores no necesita implementar todos los métodos y admite todas las conversiones que se indican en la tabla anterior. Las construcciones que no se implementan no están disponibles en las expresiones de cálculo de ese tipo. Por ejemplo, si no desea admitir la palabra clave use en las expresiones de cálculo, puede reemplazar la definición de Use en la clase de generadores.

En el ejemplo de código siguiente, se muestra la creación y el uso de un tipo de expresión de cálculo simple que genera una salida de consola que realiza un seguimiento del progreso de la ejecución del código.

// Program.fs
open Module1

// Create the computation expression object.
let trace1 = trace {
   // A normal let expression (does not call Bind).
   let x = 1
   // A let expression that uses the Bind method.
   let! y = 2
   let sum = x + y
   // return executes the Return method.
   return sum  
   }

// Execute the code. Start with the Delay method.
let result = trace1()

El código siguiente implementa la clase de generador para la expresión de cálculo de seguimiento.

// Module1.fs
module Module1 =

 // Functions that implement the builder methods.
 let bind value1 function1 = 
     printfn "Binding %A." value1 
     function1 value1

 let result value1 =
     printfn "Returning result: %A" value1
     fun () -> value1

 let delay function1 =
     fun () -> function1()

 // The builder class for the "trace" workflow.
 type TraceBuilder() =
     member x.Bind(value1, function1) = 
         bind value1 function1
     member x.Return(value1)  = result value1
     member x.Delay(function1)   = 
         printfn "Starting traced execution."
         delay function1

 let trace = new TraceBuilder()

La salida de este ejemplo es la siguiente.

Starting traced execution.
Binding 2.
Returning result: 3

Vea también

Otros recursos

Referencia del lenguaje F#

Historial de cambios

Fecha

Historial

Motivo

Diciembre de 2010

Se ha agregado el método Run y la tabla de conversiones.

Comentarios de los clientes.

1 de abril de 2011

Se ha agregado Yield y una signatura actualizada para For en la tabla de métodos de generación.

Corrección de errores de contenido.