Compartilhar via


Citações de código (F#)

Este tópico descreve cotações de código, um recurso de linguagem que permite que você gere e trabalhar com expressões de código F# programaticamente. Esse recurso permite gerar uma árvore de sintaxe abstrata que representa o código F#. A árvore de sintaxe abstrata pode ser percorrido e processada de acordo com as necessidades do seu aplicativo. Por exemplo, você pode usar a árvore para gerar o código de F# ou gerar código em alguma outra linguagem.

Expressões entre aspas

A entre aspas expressão é uma expressão F# que é delimitado de tal forma que ele não é compilado como parte do seu programa, mas em vez disso, é compilado em um objeto que representa uma expressão F# em seu código. Você pode marcar uma expressão entre aspas em uma das seguintes maneiras: com informações de tipo ou sem informações de tipo. Se você quiser incluir informações de tipo, você usar os símbolos<@ e @> para delimitar a expressão entre aspas. Se você não precisar de informações de tipo, você usar os símbolos <@@ e @@>. O código a seguir mostra tipadas e cotações.

open Microsoft.FSharp.Quotations
// A typed code quotation. 
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation. 
let expr2 : Expr = <@@ 1 + 1 @@>

Percorrer uma árvore de expressão grande é mais rápido se você não incluir informações de tipo. O tipo resultante de uma expressão entre aspas com os símbolos digitados é Expr<'T>, onde o parâmetro de tipo tem o tipo da expressão, conforme determinado pelo algoritmo de inferência de tipo do compilador F#. Quando você usa cotações de código sem informações de tipo, o tipo da expressão entre aspas é o tipo não genérico Expr. Você pode chamar o Raw propriedade no digitado Expr classe para obter sem o tipo Expr objeto.

Há uma variedade de métodos estáticos que permitem que você gerar F# objetos da expressão programaticamente o Expr classe sem o uso de expressões entre aspas.

Observe que uma cotação de código deve incluir uma expressão completa. Para um let de vinculação, por exemplo, você precisa tanto a definição do limite e uma expressão adicional que usa a ligação. Na sintaxe detalhada, isso é uma expressão que segue a in palavra-chave. No nível superior em um módulo, esta é a próxima expressão no módulo, mas em uma cotação, seja explicitamente necessário.

Portanto, a expressão a seguir não é válida.

// Not valid:
// <@ let f x = x + 1 @>

Mas as expressões a seguir são válidas.

// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@ 
    let f x = x + 10
    f 20
@>

Para usar as cotações de código, você deve adicionar uma declaração de importação (usando o open palavra-chave) que abre o Microsoft.FSharp.Quotations espaço para nome.

O PowerPack F# oferece suporte para avaliar e executar F# objetos da expressão.

Tipo de expr

Uma instância de Expr tipo representa uma expressão de F#. O genérico e não genérica Expr tipos estão documentados na documentação da biblioteca F#. Para obter mais informações, consulte Namespace Microsoft.FSharp.Quotations (F#) e Classe Quotations.Expr (F#).

Operadores splicing

Splicing permite que você combine as cotações de código literal com expressões que você criou através de programação ou de cotação de outro código. O % e %% operadores permitem que você adicionar um objeto de expressão F# em uma cotação de código. Você pode usar o % operador para inserir um objeto de expressão digitado em uma cotação digitada; Você pode usar o %% operador para inserir um objeto sem tipo de expressão em uma cotação sem tipo. Os dois operadores sejam unários prefixo. Assim, se expr é uma expressão sem tipo do tipo Expr, o código a seguir é válido.

<@@ 1 + %%expr @@>

E se expr é uma cotação digitada do tipo Expr<int>, o código a seguir é válido.

<@ 1 + %expr @>

Exemplo

Descrição

O exemplo a seguir ilustra o uso de cotações de código para colocar o código F# em um objeto de expressão e, em seguida, imprimir o código do F# que representa a expressão. Uma função println é definida que contém uma função recursiva print que exibe um objeto de expressão F# (do tipo Expr) em um formato amigável. Existem vários padrões ativos na Microsoft.FSharp.Quotations.Patterns e Microsoft.FSharp.Quotations.DerivedPatterns módulos que podem ser usados para analisar objetos de expressão. Este exemplo não inclui todos os padrões possíveis que podem aparecer em uma expressão F#. Qualquer não reconhecido padrão será disparado uma correspondência para o padrão de caractere curinga (_) e é processado usando o ToString método, que, na Expr digitar, permite que você sabe que o padrão de ativo para adicionar a sua expressão de correspondência.

Código

module Print
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns

let println expr =
    let rec print expr =
        match expr with
        | Application(expr1, expr2) ->
            // Function application.
            print expr1
            printf " "
            print expr2
        | SpecificCall <@@ (+) @@> (_, _, exprList) ->
            // Matches a call to (+). Must appear before Call pattern.
            print exprList.Head
            printf " + "
            print exprList.Tail.Head
        | Call(exprOpt, methodInfo, exprList) ->
            // Method or module function call. 
            match exprOpt with
            | Some expr -> print expr
            | None -> printf "%s" methodInfo.DeclaringType.Name
            printf ".%s(" methodInfo.Name
            if (exprList.IsEmpty) then printf ")" else
            print exprList.Head
            for expr in exprList.Tail do
                printf ","
                print expr
            printf ")"
        | Int32(n) ->
            printf "%d" n
        | Lambda(param, body) ->
            // Lambda expression.
            printf "fun (%s:%s) -> " param.Name (param.Type.ToString())
            print body
        | Let(var, expr1, expr2) ->
            // Let binding. 
            if (var.IsMutable) then
                printf "let mutable %s = " var.Name
            else
                printf "let %s = " var.Name
            print expr1
            printf " in "
            print expr2
        | PropertyGet(_, propOrValInfo, _) ->
            printf "%s" propOrValInfo.Name
        | String(str) ->
            printf "%s" str
        | Value(value, typ) ->
            printf "%s" (value.ToString())
        | Var(var) ->
            printf "%s" var.Name
        | _ -> printf "%s" (expr.ToString())
    print expr
    printfn "" 


let a = 2

// exprLambda has type "(int -> int)".
let exprLambda = <@ fun x -> x + 1 @>
// exprCall has type unit. 
let exprCall = <@ a + 1 @>

println exprLambda
println exprCall
println <@@ let f x = x + 10 in f 10 @@>

Saída

fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10

Exemplo

Descrição

Você também pode usar os três padrões ativos no módulo de ExprShape atravessar árvores de expressão com padrões de menos ativos. Esses padrões ativos podem ser útil quando você quiser percorrer uma árvore, mas você não precisa todas as informações na maioria de nós. Quando você usa esses padrões, qualquer expressão F# corresponde a um dos três seguintes padrões: ShapeVar se a expressão for uma variável, ShapeLambda se a expressão for uma expressão lambda, ou ShapeCombination se a expressão é qualquer outra coisa. Se você percorrer uma árvore de expressão utilizando padrões ativos como no exemplo de código anterior, você precisa usar muitos mais padrões para lidar com todos os F# expressão tipos possíveis e seu código será mais complexo. Para obter mais informações, consulte Padrão ativo ExprShape.ShapeVar|ShapeLambda|ShapeCombination (F#).

O exemplo de código a seguir pode ser usado como base para passagens de mais complexas. Nesse código, uma árvore de expressão é criada para uma expressão que envolve uma chamada de função, add. O SpecificCall active padrão é usado para detectar qualquer chamada para add na árvore de expressão. Esse padrão active atribui os argumentos da chamada para o exprList valor. Nesse caso, há apenas dois, para que elas são puxadas para fora e a função é chamada recursivamente sobre os argumentos. Os resultados são inseridos em uma cotação de código que representa uma chamada para mul usando o operador splice (%%). O println a função do exemplo anterior é usada para exibir os resultados.

O código em outras ramificações de padrão ativo regenera apenas a mesma árvore de expressão, portanto, a única alteração na expressão resultante é a mudança do add para mul.

Código

module Module1
open Print
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.ExprShape

let add x y = x + y
let mul x y = x * y

let rec substituteExpr expression =
    match expression with
    | SpecificCall <@@ add @@> (_, _, exprList) ->
        let lhs = substituteExpr exprList.Head
        let rhs = substituteExpr exprList.Tail.Head
        <@@ mul %%lhs %%rhs @@>
    | ShapeVar var -> Expr.Var var
    | ShapeLambda (var, expr) -> Expr.Lambda (var, substituteExpr expr)
    | ShapeCombination(shapeComboObject, exprList) ->
        RebuildShapeCombination(shapeComboObject, List.map substituteExpr exprList)

let expr1 = <@@ 1 + (add 2 (add 3 4)) @@>
println expr1
let expr2 = substituteExpr expr1
println expr2

Saída

1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))

Consulte também

Outros recursos

Referência da linguagem F#