Que fait Expression.Reduce ()?

Je travaille avec des arbres d’expression depuis quelques jours maintenant et je suis curieux de savoir ce que fait Expression.Reduce (). La documentation de msdn n’est pas très utile car elle indique seulement qu’elle “réduit” l’expression. Juste au cas où, j’ai essayé un exemple (voir ci-dessous) pour vérifier si cette méthode comprenait une réduction mathématique, mais cela ne semble pas être le cas.

Est-ce que quelqu’un sait ce que fait cette méthode et est-il possible de donner un exemple rapide montrant qu’elle est en action? De bonnes ressources là-bas?

static void Main(ssortingng[] args) { Expression<Func> func = x => (x + x + x) + Math.Exp(x + x + x); Console.WriteLine(func); Expression r_func = func.Reduce(); Console.WriteLine(r_func); // This prints out the same as Console.WriteLine(func) } 

Le document à examiner est “expr-tree-spec.doc” ici: http://dlr.codeplex.com/wikipage?title=Docs%20and%20specs&referringTitle=Documentation

Ceci est la spécification pour les arbres d’expression. Lisez les sections “2.2 Nodes réductibles” et “4.3.5 Méthode de réduction”.

Cette méthode est essentiellement destinée aux personnes qui implémentent ou portent leur langage dynamic vers .NET. Ils peuvent ainsi créer leurs propres nœuds pouvant être réduits à des nœuds d’arborescence d’expression standard et pouvant être compilés. Il y a quelques nœuds “réductibles” dans l’API des arbres d’expression, mais je ne sais pas si vous pouvez obtenir des exemples pratiques (puisque tous les nœuds d’expression standard se comstacknt de toute façon, en tant qu’utilisateur final, vous ne vous souciez probablement pas de savoir s’ils sont “réduits” “dans les coulisses ou non).

Oui, la documentation MSDN est très basique dans ce domaine, car la principale source d’informations et de documents pour les développeurs de langage est http://dlr.codeplex.com/

Avec un peu de déassembly, j’ai trouvé que Expression.CanReduce renvoyait toujours la valeur false et que Expression.Reduce () le retournait toujours. Cependant, il existe quelques types qui remplacent les deux. LambdaExpression hérite des implémentations par défaut, ce qui explique pourquoi les expressions essayées jusqu’à présent ne fonctionnent pas.

MemberInitExpression est l’un des types qui redéfinit Réduction (), ce qui m’a conduit à l’expérience réussie suivante:

 class ReduceFinder : ExpressionVisitor { public override Expression Visit(Expression node) { if (node != null && node.CanReduce) { var reduced = node.Reduce(); Console.WriteLine("Found expression to reduce!"); Console.WriteLine("Before: {0}: {1}", node.GetType().Name, node); Console.WriteLine("After: {0}: {1}", reduced.GetType().Name, reduced); } return base.Visit(node); } } class Foo { public int x; public int y; } static class Program { static void Main() { Expression> expr = z => new Foo { x = (z + 1), y = (z + 1) }; new ReduceFinder().Visit(expr); } } 

Sortie:

 Found expression to reduce! Before: MemberInitExpression: new Foo() {x = (z + 1), y = (z + 1)} After: ScopeN: { ... } 

C’est une question assez ancienne, mais elle semble avoir un peu d’intérêt, et j’ajoute donc cette réponse supplémentaire avec des informations sur ce que les éléments .NET prêts à l’emploi font à l’heure actuelle.

Autant que je sache, réduire () n’est substitué que dans les opérations complexes qui implémentent une affectation dans le cadre de leur travail. Il semble y avoir trois scénarios clés.

  1. Les affectations composées sont étendues aux opérations arithmétiques et d’affectation binarys discrètes; en d’autres termes,

    x += y

    devient

    x = x + y .

  2. Les opérateurs de pré-incrémentation et de post-incrémentation sont étendus à leurs opérations discrètes. Pour les pré-incréments / décréments,

    ++x

    devient approximativement:

    x = x + 1

    et

    x++

    devient approximativement:

     temp = x; x = x + 1; temp; 

    Je dis approximativement parce que l’opération n’est pas implémentée comme une opération binary x + 1 l’opérande gauche étant x et l’opérande droit étant la constante 1 mais comme une opération d’incrémentation / décrémentation unaire. L’effet net est le même.

  3. Les initialiseurs de membres et de listes sont étendus de leur forme courte à leur forme longue. Alors:

    new Thing() { Param1 = 4, Param2 = 5 }

    devient:

     temp = new Thing(); temp.Param1 = 4; temp.Param2 = 5; temp; 

    et:

    new List() { 4, 5 }

    devient:

     temp = new List(); temp.Add(4); temp.Add(5); temp; 

Que ces modifications rendent plus facile ou plus difficile pour une personne d’implémenter quelque chose qui parsing un arbre d’expression est une question d’opinion, mais au bout du compte, c’est le niveau de réduction qui semble sortir de la boîte dans le cadre .NET.

Suite à la réponse de Nick Guerrera , j’ai trouvé les expressions suivantes qui ont remplacé la méthode CanReduce :

  • AssignBinaryExpression *
  • Expression binary
  • CoalesceConversionBinaryExpression *
  • LogicalBinaryExpression *
  • SimpleBinaryExpression *
  • ListInitExpression
  • MembreInitExpression
  • Expression unaire

* Indique un type dérivé interne de BinaryExpression selon JustDecomstack

J’imagine qu’il est plus difficile pour différents fournisseurs de linq d’utiliser ceux-ci pour transformer certains types de nœuds en une représentation plus simple.

étant donné que les documents sont rares, ils pourraient être utilisés pour éliminer les sous-expressions courantes et éliminer les expressions redondantes. si votre fonction calcule x + x plusieurs fois sans changer le x local, vous pouvez la simplifier en enregistrant le résultat de la première expression dans une variable temporaire. peut-être que ce serait au fournisseur linq d’implémenter éventuellement ces transformations.

ou si vous aviez nested des BlockExpressions ne contenant aucun code (une expression comme {{{}}}), celles-ci pourraient être éliminées ou un ConditionalExpression vide …