Compartilhar via


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

Este tópico descreve cotações de código, um recurso de linguagem que permite gerar 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 citada 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 duas maneiras: ou, 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 não precisar de informações de tipo, você usar os símbolos <@@ e @@>. O código a seguir mostra as cotações tipadas e.

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

Como 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 de expressão, conforme determinado pelo algoritmo de inferência do tipo. do compilador F# Quando você usa cotações de código sem informações de tipo, o tipo de expressão entre aspas é o tipo não genérico Expr. Você pode chamar o Raw propriedade em que o digitado Expr classe para obter o tipados Expr objeto.

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

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 de nome de ligação e de 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 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 F# PowerPack fornece suporte para avaliar e executar o F# objetos de expressão.

Tipo de expr

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

Operadores splicing

Splicing permite combinar 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 % o operador para inserir um objeto de expressão digitado em uma cotação digitado; Você pode usar o %% operador para inserir um objeto sem tipo de expressão 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 de F# em um objeto de expressão e 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 expressão. Este exemplo não inclui todos os padrões possíveis que podem aparecer em uma expressão de F#. Qualquer não reconhecido padrão dispara uma correspondência para o padrão de caractere curinga (_) e é processado usando o ToString método, que, sobre o Expr Digite, permite que você conhece 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 para atravessar árvores de expressão com menos de padrões de ativos. Esses padrões ativos podem ser útil quando você deseja percorrer uma árvore, mas todas as informações na maioria de nós não é necessário. Quando você usa esses padrões, qualquer expressão F# corresponde a uma das três seguintes padrões: ShapeVarSe 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 usando os padrões de 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 ExprShape.ShapeVar|ShapeLambda|Padrão de ativos de ShapeCombination (F#).

O exemplo de código a seguir pode ser usado como base para as passagens 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 padrão ativo é 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 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 apenas regenera 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 de linguagem do F#

Histórico de alterações

Date

History

Motivo

Maio de 2010

Corrija o último exemplo de código.

Correção de bug de conteúdo.