Почему FingerTrees не используются достаточно для стабильной реализации?
Некоторое время назад я наткнулся на статью о FingerTrees (см. также сопровождающий стек Вопрос переполнения) и подал идею. Я наконец нашел причину, чтобы использовать их.
Моя проблема заключается в том, что Data.FingerTree пакет, кажется, немного гниет по краям. Кроме того, Data.Sequence в пакете Containers, который использует структуру данных повторно реализует (возможно, лучшую) версию, но не экспортирует ее.
Как теоретически полезно, как кажется, эта структура кажется, она, кажется, не получает много фактического использования или внимания. Обнаружили ли люди, что FingerTrees не полезны в практическом плане или это недостаточно для внимания?
дальнейшее объяснение:
Я заинтересован в создании структуры структуры данных, содержащей хорошие свойства конкатенации. Подумайте о создании HTML-документа из различных фрагментов. Большинство готовых решений используют bytestrings, но я действительно хочу что-то, что имеет дело с текстом Unicode должным образом. Мой план на данный момент состоит в том, чтобы сложить фрагменты Data.Text в FingerTree.
Я также хотел бы заимствовать трюк из Data.Vector для приема фрагментов без копирования с использованием (смещения, длины) манипуляции. Data.Text.Text имеет этот встроенный тип данных, но использует его только для эффективных операций uncons и unsnoc. В FingerTree эта информация может очень легко стать v
или аннотацией дерева.
Ответы
Ответ 1
Чтобы ответить на ваш вопрос о пальцах в частности, я думаю, проблема заключается в том, что они имеют относительно высокие постоянные затраты по сравнению с массивами и более сложны, чем другие способы достижения эффективной конкатенации. У Builder есть более эффективный интерфейс для добавления только кусков, и они обычно легко доступны (см. Ссылки в ответе @informatikr). Предположим, что Data.Text.Lazy
реализован со связанным списком кусков, и вы создаете Data.Text.Lazy
из построителя. Если у вас много кусков (возможно, более 50) или многократно обращаются к данным в конце списка, высокая постоянная стоимость пальцевого дерева, вероятно, не стоит.
Реализация Data.Sequence
специализирована по соображениям производительности и не является общей, как полный интерфейс, предоставляемый пакетом fingertree
. Вот почему он не экспортируется; это действительно невозможно использовать для чего-либо другого, кроме Sequence
.
Я также подозреваю, что многие программисты не понимают, как на самом деле использовать моноидальную аннотацию, поскольку она стоит за довольно значительным барьером абстракции. Так много людей не будут использовать его, потому что они не видят, как это может быть полезно по сравнению с другими типами данных.
Я действительно не понял это, пока не прочитал серию блога Chung-chieh Shan на номерах слов (part2, part3, part4). Это доказательство того, что идея может быть использована в практическом коде.
В вашем случае, если вам нужно как проверять частичные результаты, так и иметь эффективные добавления, использование кончика пальца может быть лучше, чем строитель. В зависимости от реализации построителя вы можете в конечном итоге выполнить много повторной работы при преобразовании в Text
, добавить больше материала в конструктор, снова преобразовать в Text
и т.д. Однако это будет зависеть от вашего шаблона использования.
Вам может быть интересен мой splaytree пакет, который предоставляет деревьям многолучевых аннотаций, а на них построены несколько разных структур. Помимо самого дерева splay, модули Set
и RangeSet
имеют более или менее полный API, модуль Sequence
- это в основном скелет, который я использовал для тестирования. Это не решение "с батарейками", которое вы ищете (опять-таки, @informatikr отвечает), но если вы хотите экспериментировать с моноидальными аннотациями, это может быть более полезно, чем Data.FingerTree
. Имейте в виду, что дерево воспроизведения может быть неуравновешенным, если вы перемещаете все элементы в последовательности (или постоянно snoc на конце или аналогично), но если добавление и поиск чередуются, производительность может быть отличной.
Ответ 2
В дополнение к ответу на вопрос Джона Лато я добавлю некоторые конкретные подробности о производительности пальцевых деревьев, поскольку я потратил некоторое время на то, чтобы посмотреть на это в прошлом.
Широкое резюме:
-
Data.Sequence
имеет большие постоянные факторы и асимптотику: он почти так же быстро, как и []
при доступе к фронту списка (где обе структуры данных имеют асимптотику O (1)) и намного быстрее в другом месте список (где Data.Sequence
логарифмическая асимптотика trounce []
линейная асимптотика).
-
Data.FingerTree
имеет ту же асимптотику, что и Data.Sequence
, но примерно на порядок медленнее.
Подобно спискам, пальцевые деревья имеют большие накладные расходы на элементарные элементы, поэтому их следует комбинировать с chunking для лучшей памяти и использования кеша. Действительно, несколько пакетов делают это (yi, trifecta, rope). Если Data.FingerTree
можно было бы приблизить к Data.Sequence
в производительности, я бы надеялся увидеть тип Data.Text.Sequence
, который реализовал пальцевое дерево значений Data.Text
. Такой тип потерял бы потоковое поведение Data.Text.Lazy
, но выиграл от улучшения производительности произвольного доступа и конкатенации. (Точно так же я хотел бы видеть Data.ByteString.Sequence
и Data.Vector.Sequence
.)
Препятствие для их реализации теперь заключается в том, что не существует эффективных эффективных и общих реализации пальцевых деревьев (см. ниже, где я обсуждаю это далее). Для создания эффективных реализаций Data.Text.Sequence
нужно было бы полностью переопределить деревья пальцев, специализированные на Text
- так же, как Data.Text.Lazy
полностью переименовывает списки, специализированные на Text
. К сожалению, деревья пальцев намного сложнее, чем списки (особенно Тип меры распаковывается в конструктор Deep
, который сохраняет разметки указателя во внутренних циклах операций дерева.
Эти оптимизации можно применить в общем случае Data.FingerTree
, используя семейства данных для общей распаковки и используя GHC inliner и specialiser - см. мой fingerertree-unboxed package, который обеспечивает общую производительность пальцев пальца почти до уровня Data.Sequence
. К сожалению, эти методы имеют некоторые существенные проблемы:
-
семейства данных для общей распаковки неприятны для пользователя, поскольку они должны определять множество экземпляров. Нет четкого решения этой проблемы.
-
В пальцах используются полиморфные рекурсии, которые GHC-специалист не справляется хорошо (1, 2). Это означает, что для получения достаточной специализации по типу меры нам нужно много INLINE
прагм, что заставляет GHC генерировать огромное количество кода.
Из-за этих проблем я никогда не выпускал пакет в Hackage.
Ответ 3
Игнорирование вашего вопроса о пальцевом дереве и ответ на ваше дальнейшее объяснение: просмотрели ли вы Data.Text.Lazy.Builder или, в частности, для создания HTML, blaze-html?
Оба допускают быструю конкатенацию. Для нарезки, если это важно для решения вашей проблемы, они могут не иметь идеальной производительности.