Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article décrit les guillemets de code, une fonctionnalité de langage qui vous permet de générer et d’utiliser des expressions de code F# par programmation. Cette fonctionnalité vous permet de générer une arborescence de syntaxe abstraite qui représente le code F#. L’arborescence de syntaxe abstraite peut ensuite être parcourue et traitée en fonction des besoins de votre application. Par exemple, vous pouvez utiliser l’arborescence pour générer du code F# ou générer du code dans une autre langue.
Expressions entre guillemets
Une expression entre guillemets est une expression F# dans votre code qui est délimitée de telle manière qu’elle n’est pas compilée dans le cadre de votre programme, mais est compilée dans un objet qui représente une expression F#. Vous pouvez marquer une expression entre guillemets de deux façons : avec des informations de type ou sans informations de type. Si vous souhaitez inclure des informations de type, vous utilisez les symboles et @>
pour délimiter l’expression entre guillemets<@
. Si vous n’avez pas besoin d’informations de type, vous utilisez les symboles <@@
et @@>
. Le code suivant montre les guillemets typés et non typés.
open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>
La traversée d’une arborescence d’expressions volumineuses est plus rapide si vous n’incluez pas d’informations de type. Le type résultant d’une expression entre guillemets avec les symboles typés est Expr<'T>
, où le paramètre de type a le type de l’expression tel que déterminé par l’algorithme d’inférence de type du compilateur F#. Lorsque vous utilisez des guillemets de code sans informations de type, le type de l’expression entre guillemets est le type non générique Expr. Vous pouvez appeler la propriété Raw sur la classe typée Expr
pour obtenir l’objet non typé Expr
.
Il existe différentes méthodes statiques qui vous permettent de générer des objets d’expression F# par programmation dans la Expr
classe sans utiliser d’expressions entre guillemets.
Un guillemet de code doit inclure une expression complète. Pour une let
liaison, par exemple, vous avez besoin de la définition du nom lié et d’une autre expression qui utilise la liaison. Dans la syntaxe détaillée, il s’agit d’une expression qui suit le in
mot clé. Au niveau supérieur d’un module, il s’agit simplement de l’expression suivante dans le module, mais entre guillemets, elle est explicitement requise.
Par conséquent, l’expression suivante n’est pas valide.
// Not valid:
// <@ let f x = x + 1 @>
Toutefois, les expressions suivantes sont valides.
// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@
let f x = x + 10
f 20
@>
Pour évaluer les guillemets F#, vous devez utiliser l’évaluateur de guillemets F#. Il prend en charge l’évaluation et l’exécution d’objets d’expression F#.
Les guillemets F# conservent également les informations de contrainte de type. Prenons l’exemple suivant :
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
La contrainte générée par la inline
fonction est conservée entre guillemets de code. Le formulaire entre guillemets de la negate
fonction peut maintenant être évalué.
Type Expr
Une instance du Expr
type représente une expression F#. Les types génériques et non génériques Expr
sont documentés dans la documentation de la bibliothèque F#. Pour plus d’informations, consultez L’espace de noms FSharp.Quotations et la classe Quotations.Expr.
Opérateurs de découpage
Le découpage vous permet de combiner des guillemets de code littéral avec des expressions que vous avez créées par programmation ou à partir d’un autre guillemet de code. Les %
opérateurs et %%
les opérateurs vous permettent d’ajouter un objet d’expression F# dans un guillemet de code. Vous utilisez l’opérateur %
pour insérer un objet d’expression typée dans un guillemet typé ; vous utilisez l’opérateur %%
pour insérer un objet d’expression non typé dans un guillemet non typé. Les deux opérateurs sont des opérateurs de préfixe unaire. Par conséquent, s’il s’agit expr
d’une expression non typée de type Expr
, le code suivant est valide.
<@@ 1 + %%expr @@>
Et s’il s’agit expr
d’un guillemet typé de type Expr<int>
, le code suivant est valide.
<@ 1 + %expr @>
Exemple 1
Descriptif
L’exemple suivant illustre l’utilisation de guillemets de code pour placer le code F# dans un objet d’expression, puis imprimer le code F# qui représente l’expression. Une fonction println
est définie qui contient une fonction print
récursive qui affiche un objet d’expression F# (de type Expr
) dans un format convivial. Il existe plusieurs modèles actifs dans les modules FSharp.Quotations.Patterns et FSharp.Quotations.DerivedPatterns qui peuvent être utilisés pour analyser des objets d’expression. Cet exemple n’inclut pas tous les modèles possibles qui peuvent apparaître dans une expression F#. Tout modèle non reconnu déclenche une correspondance avec le modèle générique (_
) et est rendu à l’aide de la ToString
méthode, qui, sur le Expr
type, vous permet de connaître le modèle actif à ajouter à votre expression de correspondance.
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 @@>
Sortie
fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10
Exemple 2
Descriptif
Vous pouvez également utiliser les trois modèles actifs dans le module ExprShape pour parcourir les arborescences d’expressions avec moins de modèles actifs. Ces modèles actifs peuvent être utiles lorsque vous souhaitez parcourir une arborescence, mais vous n’avez pas besoin de toutes les informations dans la plupart des nœuds. Lorsque vous utilisez ces modèles, toute expression F# correspond à l’un des trois modèles suivants : ShapeVar
si l’expression est une variable, ShapeLambda
si l’expression est une expression lambda ou ShapeCombination
si l’expression est autre chose. Si vous parcourez une arborescence d’expressions à l’aide des modèles actifs comme dans l’exemple de code précédent, vous devez utiliser de nombreux modèles supplémentaires pour gérer tous les types d’expressions F# possibles, et votre code sera plus complexe. Pour plus d’informations, consultez ExprShape.ShapeVar|ShapeLambda|Modèle actif ShapeCombination.
L’exemple de code suivant peut être utilisé comme base pour les traversées plus complexes. Dans ce code, une arborescence d’expressions est créée pour une expression qui implique un appel de fonction. add
Le modèle actif SpecificCall est utilisé pour détecter tout appel dans add
l’arborescence d’expressions. Ce modèle actif affecte les arguments de l’appel à la exprList
valeur. Dans ce cas, il n’y a que deux, donc elles sont extraites et la fonction est appelée de manière récursive sur les arguments. Les résultats sont insérés dans un guillemet de code qui représente un appel à l’aide mul
de l’opérateur splice (%%
). La println
fonction de l’exemple précédent est utilisée pour afficher les résultats.
Le code dans les autres branches de modèle actif régénère simplement la même arborescence d’expressions, de sorte que la seule modification de l’expression résultante est la modification de add
.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
Sortie
1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))