J'ai jeté un coup d'œil à Roslyn CTP et, même si cela résout un problème similaire à Expression tree API , les deux sont immuables mais Roslyn le fait d'une manière très différente :
Les nœuds Expression
n'ont aucune référence au nœud parent, sont modifiés à l'aide d'un ExpressionVisitor
et c'est pourquoi les grandes pièces peuvent être réutilisées.
SyntaxNode
de Roslyn, de l'autre côté, a une référence à son parent, donc tous les nœuds deviennent effectivement un bloc qui est impossible à réutiliser. Des méthodes comme Update
, ReplaceNode
, etc., sont fournies pour apporter des modifications.
Où cela finit-il? Document
? Project
? ISolution
? L'API favorise un changement pas à pas de l'arborescence (au lieu d'un bouton vers le haut), mais chaque étape en fait-elle une copie complète?
Pourquoi ont-ils fait un tel choix? Y a-t-il une astuce intéressante qui me manque?
MISE À JOUR: Cette question était le sujet de mon blog le 8 juin 2012 . Merci pour la grande question!
Grande question. Nous avons débattu des questions que vous soulevez depuis très, très longtemps.
Nous aimerions avoir une structure de données qui présente les caractéristiques suivantes:
Par persistance , j'entends la capacité de réutiliser la plupart des nœuds existants dans l'arborescence lorsqu'une modification est effectuée dans le tampon de texte. Étant donné que les nœuds sont immuables, il n'y a aucune barrière à leur réutilisation. Nous en avons besoin pour la performance; nous ne pouvons pas ré-analyser d'énormes modifications du fichier chaque fois que vous appuyez sur une touche. Nous devons re-Lex et ré-analyser uniquement les parties de l'arbre qui ont été affectées par la modification.
Maintenant, lorsque vous essayez de regrouper ces cinq éléments dans une seule structure de données, vous rencontrez immédiatement des problèmes:
Mais dans l'équipe de Roslyn, nous faisons régulièrement des choses impossibles. En fait, nous faisons l'impossible en gardant deux arbres d'analyse. L'arbre "vert" est immuable, persistant, n'a aucune référence parent, est construit "de bas en haut", et chaque nœud suit sa largeur mais pas sa position absolue . Lorsqu'une modification se produit, nous reconstruisons uniquement les parties de l'arbre vert qui ont été affectées par la modification, qui correspond généralement à O (log n) du nombre total de nœuds d'analyse dans l'arborescence.
L'arbre "rouge" est une façade immuable qui est construite autour de l'arbre vert; il est construit "top-down" à la demande et jeté à chaque édition. Il calcule les références parentales en les fabriquant à la demande lorsque vous descendez dans l'arborescence du haut . Il fabrique des positions absolues en les calculant à partir des largeurs, à nouveau, lorsque vous descendez.
Vous, l'utilisateur, ne voyez que l'arbre rouge; l'arbre vert est un détail d'implémentation. Si vous regardez dans l'état interne d'un nœud d'analyse, vous verrez en fait qu'il y a une référence à un autre nœud d'analyse d'un type différent; c'est le nœud de l'arbre vert.
Soit dit en passant, ceux-ci sont appelés "arbres rouges/verts" car ce sont les couleurs des marqueurs de tableau blanc que nous avons utilisées pour dessiner la structure des données lors de la réunion de conception. Il n'y a pas d'autre sens aux couleurs.
L'avantage de cette stratégie est que nous obtenons toutes ces grandes choses: immuabilité, persistance, références parentales, etc. Le coût est que ce système est complexe et peut consommer beaucoup de mémoire si les façades "rouges" deviennent grandes. Nous faisons actuellement des expériences pour voir si nous pouvons réduire certains des coûts sans perdre les avantages.