UPDATE: Этот вопрос был предметом моего блога на 8 июня 2012 года . Спасибо за отличный вопрос!
Отличный вопрос. Мы долго-долго обсуждали поднимаемые вами вопросы.
Мы хотели бы иметь структуру данных со следующими характеристиками:
- Неизменный.
- Форма дерева.
- Дешевый доступ к родительским узлам из дочерних узлов.
- Возможно отображение узла в дереве на символьное смещение в тексте.
- Стойкий .
Под настойчивостью я подразумеваю возможность повторно использовать большинство существующих узлов в дереве при редактировании текстового буфера. Поскольку узлы неизменяемы, нет никаких препятствий для их повторного использования. Нам это нужно для производительности; мы не можем повторно анализировать огромные блоки файла каждый раз, когда вы нажимаете клавишу. Нам нужно повторно проанализировать и проанализировать только те части дерева, которые были затронуты редактированием.
Теперь, когда вы пытаетесь поместить все эти пять вещей в одну структуру данных, вы сразу же сталкиваетесь с проблемами:
- Как вы в первую очередь создаете узел? И родитель, и потомок ссылаются друг на друга и неизменны, поэтому какой из них будет построен первым?
- Предположим, вам удастся решить эту проблему: как сделать ее постоянной? Вы не можете повторно использовать дочерний узел в другом родителе, потому что это потребует сообщения дочернему узлу о том, что у него новый родительский узел. Но ребенок неизменен.
- Предположим, вам удалось решить эту проблему: когда вы вставляете новый символ в буфер редактирования, изменяется абсолютная позиция каждого узла, который сопоставлен с позицией после этой точки . Это очень усложняет создание устойчивой структуры данных, потому что любое редактирование может изменить интервалы большинства узлов!
Но в команде Roslyn мы постоянно делаем невозможные вещи. На самом деле мы делаем невозможное, сохраняя два дерева синтаксического анализа. «Зеленое» дерево является неизменным, постоянным, не имеет родительских ссылок, строится «снизу вверх», и каждый узел отслеживает его ширину, но не его абсолютное положение . Когда происходит редактирование, мы перестраиваем только те части зеленого дерева, которые были затронуты редактированием, что обычно составляет около O (log n) от общего числа узлов синтаксического анализа в дереве.
«Красное» дерево - это неизменный фасад , построенный вокруг зеленого дерева; он создается по запросу «сверху вниз» и выбрасывается при каждом редактировании. Он вычисляет родительские ссылки, производя их по запросу, когда вы спускаетесь по дереву сверху . Он определяет абсолютные позиции, вычисляя их по ширине, опять же, когда вы спускаетесь.
Вы, пользователь, видите только красное дерево; зеленое дерево - деталь реализации. Если вы заглянете во внутреннее состояние узла синтаксического анализа, вы фактически увидите, что там есть ссылка на другой узел синтаксического анализа другого типа; это зеленый узел дерева.
Между прочим, они называются «красными / зелеными деревьями», потому что это были цвета маркеров белой доски, которые мы использовали для рисования структуры данных на совещании по дизайну. У цветов нет другого значения.
Преимущество этой стратегии в том, что мы получаем все эти замечательные вещи: неизменяемость, постоянство, родительские ссылки и так далее. Цена состоит в том, что эта система сложна и может потреблять много памяти, если «красные» фасады становятся большими. В настоящее время мы проводим эксперименты, чтобы увидеть, сможем ли мы снизить некоторые затраты без потери выгод.