ACTUALIZACIÓN: Esta pregunta fue el tema de mi blog el 8 de junio de 2012 . Gracias por la gran pregunta!
Gran pregunta Debatimos los problemas que plantea durante mucho, mucho tiempo.
Nos gustaría tener una estructura de datos que tenga las siguientes características:
- Inmutable.
- La forma de un árbol.
- Acceso barato a los nodos principales desde los nodos secundarios.
- Posible asignar desde un nodo en el árbol a un desplazamiento de caracteres en el texto.
- Persistente .
Por persistencia me refiero a la capacidad de reutilizar la mayoría de los nodos existentes en el árbol cuando se realiza una edición en el búfer de texto. Como los nodos son inmutables, no hay ninguna barrera para reutilizarlos. Necesitamos esto para el rendimiento; no podemos volver a analizar grandes cantidades de archivos cada vez que presionas una tecla. Necesitamos volver a analizar y volver a analizar solo las partes del árbol que fueron afectadas por la edición.
Ahora, cuando intentas poner las cinco cosas en una estructura de datos, inmediatamente tienes problemas:
- ¿Cómo se construye un nodo en primer lugar? El padre y el hijo se refieren entre sí y son inmutables, entonces, ¿cuál se construye primero?
- Supongamos que logras resolver ese problema: ¿cómo lo haces persistente? No puede reutilizar un nodo hijo en un padre diferente porque eso implicaría decirle al niño que tiene un nuevo padre. Pero el niño es inmutable.
- Supongamos que logra resolver ese problema: cuando inserta un nuevo carácter en el búfer de edición, la posición absoluta de cada nodo que se asigna a una posición después de ese punto cambia. ¡Esto hace que sea muy difícil crear una estructura de datos persistente, porque cualquier edición puede cambiar la extensión de la mayoría de los nodos!
Pero en el equipo de Roslyn hacemos rutinariamente cosas imposibles. Realmente hacemos lo imposible al mantener dos árboles de análisis. El árbol "verde" es inmutable, persistente, no tiene referencias primarias, está construido "de abajo hacia arriba" y cada nodo rastrea su ancho pero no su posición absoluta . Cuando ocurre una edición, reconstruimos solo las partes del árbol verde que fueron afectadas por la edición, que generalmente se trata de O (log n) del total de nodos de análisis en el árbol.
El árbol "rojo" es una fachada inmutable que se construye alrededor del árbol verde; se construye "de arriba hacia abajo" bajo demanda y se desecha en cada edición. Calcula referencias de padres al fabricarlas a pedido a medida que desciende a través del árbol desde la parte superior . Fabrica posiciones absolutas al calcularlas a partir de los anchos, nuevamente, a medida que desciende.
Usted, el usuario, solo ve el árbol rojo; El árbol verde es un detalle de implementación. Si observa el estado interno de un nodo de análisis, de hecho verá que hay una referencia a otro nodo de análisis de un tipo diferente; ese es el nodo del árbol verde.
Por cierto, estos se llaman "árboles rojos / verdes" porque esos fueron los colores de marcador de pizarra que utilizamos para dibujar la estructura de datos en la reunión de diseño. No hay otro significado para los colores.
El beneficio de esta estrategia es que obtenemos todas esas grandes cosas: inmutabilidad, persistencia, referencias de los padres, etc. El costo es que este sistema es complejo y puede consumir mucha memoria si las fachadas "rojas" se agrandan. Actualmente estamos haciendo experimentos para ver si podemos reducir algunos de los costos sin perder los beneficios.