Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este artigo descreve aspas de código, um recurso de linguagem que permite que você gere e trabalhe 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 percorrida e processada de acordo com as necessidades do aplicativo. Por exemplo, você pode usar a árvore para gerar código F# ou gerar código em algum outro idioma.
Expressões entre aspas
Uma expressão entre aspas é uma expressão F# em seu código que é delimitada de forma que não seja compilada como parte do seu programa, mas, em vez disso, é compilada em um objeto que representa uma expressão F#. Você pode marcar uma expressão entre aspas de duas maneiras: com informações de tipo ou sem informações de tipo. Se você quiser incluir informações de tipo, use os símbolos e @> delimita a expressão entre aspas<@. Se você não precisar de informações de tipo, use os símbolos <@@ e @@>. O código a seguir mostra aspas digitados e não tipados.
open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>
Atravessar uma árvore de expressão grande será mais rápido se você não incluir informações de tipo. O tipo resultante de uma expressão entre os símbolos tipados é Expr<'T>, em que 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 aspas 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 a propriedade Raw na classe tipada Expr para obter o objeto não tipado Expr .
Há vários métodos estáticos que permitem gerar objetos de expressão F# programaticamente na Expr classe sem usar expressões entre aspas.
Uma aspa de código deve incluir uma expressão completa. Para uma let associação, por exemplo, você precisa da definição do nome associado e de outra expressão que use a associação. Na sintaxe detalhada, essa é uma expressão que segue a in palavra-chave. No nível superior de um módulo, essa é apenas a próxima expressão no módulo, mas, entre aspas, ela é explicitamente necessária.
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 avaliar as aspas de F#, você deve usar o Avaliador de Aspas F#. Ele fornece suporte para avaliar e executar objetos de expressão F#.
As aspas F# também retêm informações de restrição de tipo. Considere o seguinte exemplo:
open FSharp.Linq.RuntimeHelpers
let eval q = LeafExpressionConverter.EvaluateQuotation q
let inline negate x = -x
// val inline negate: x: ^a -> ^a when ^a : (static member ( ~- ) : ^a -> ^a)
<@ negate 1.0 @> |> eval
A restrição gerada pela inline função é mantida nas aspas de código. O negate formulário entre aspas da função agora pode ser avaliado.
Tipo expr
Uma instância do Expr tipo representa uma expressão F#. Os tipos genéricos e não genéricos Expr estão documentados na documentação da biblioteca F#. Para obter mais informações, consulte a classe FSharp.Quotations Namespace and Quotations.Expr.
Operadores de splicing
O splicing permite combinar aspas de código literal com expressões que você criou programaticamente ou de outra aspas de código. Os % operadores e %% os operadores permitem que você adicione um objeto de expressão F# a uma aspa de código. Use o % operador para inserir um objeto de expressão tipado em uma aspa digitada; use o %% operador para inserir um objeto de expressão não tipado em uma aspa não tipada. Ambos os operadores são operadores de prefixo unário. Portanto, se expr for uma expressão de tipo Exprnão tipada, o código a seguir será válido.
<@@ 1 + %%expr @@>
E se expr for uma aspa digitada do tipo Expr<int>, o código a seguir será válido.
<@ 1 + %expr @>
Exemplo 1
Descrição
O exemplo a seguir ilustra o uso de aspas de código para colocar o código F# em um objeto de expressão e imprimir o código F# que representa a expressão. Uma função println é definida que contém uma função print recursiva que exibe um objeto de expressão F# (do tipo Expr) em um formato amigável. Há vários padrões ativos nos módulos FSharp.Quotations.Patterns e FSharp.Quotations.DerivedPatterns 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 padrão não reconhecido dispara uma correspondência com o padrão curinga (_) e é renderizado usando o ToString método, que, no Expr tipo, permite que você saiba o padrão ativo a ser adicionado à expressão de correspondência.
Code
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 2
Descrição
Você também pode usar os três padrões ativos no módulo ExprShape para percorrer árvores de expressão com menos padrões ativos. Esses padrões ativos podem ser úteis quando você deseja atravessar uma árvore, mas você não precisa de todas as informações na maioria dos nós. Quando você usa esses padrões, qualquer expressão F# corresponde a um dos três padrões a seguir: 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 for qualquer outra coisa. Se você percorrer uma árvore de expressão usando os padrões ativos como no exemplo de código anterior, precisará usar muito mais padrões para lidar com todos os tipos de expressão F# possíveis e seu código será mais complexo. Para obter mais informações, consulte ExprShape.ShapeVar|ShapeLambda|Padrão Ativo ShapeCombination.
O exemplo de código a seguir pode ser usado como base para 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 padrão ativo SpecificCall é usado para detectar qualquer chamada add na árvore de expressão. Esse padrão ativo atribui os argumentos da chamada ao exprList valor. Nesse caso, há apenas dois, portanto, eles são retirados e a função é chamada recursivamente nos argumentos. Os resultados são inseridos em uma aspa de código que representa uma chamada usando mul o operador splice (%%). A println função do exemplo anterior é usada para exibir os resultados.
O código nos outros branches de padrão ativo apenas regenera a mesma árvore de expressão, portanto, a única alteração na expressão resultante é a alteração de add para mul.
Code
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))