Подтвердить что ты не робот

Finger Tree (Data.Sequence) vs Rope (Data.Rope) (Haskell или вообще)

Каковы ключевые различия между Finger Tree (Data.Sequence) и канатом (Data.Rope) (версия Эдварда Кемта или версия Пьера-Этьена Менье >

В библиотеках Haskell Data.Sequence имеет больше функций. Я думаю, что веревки обрабатывают "блоки" более эффективно.

Как программист оценил эффективность, скажем, последовательность из 7 миллионов символов, где мне нужно (а) вставить где угодно, (б) вырезать и вставлять сегменты (сращивание), (в) искать и заменять подстроки, что более эффективно?

Разъяснения в ответ на ehird:

  • Основная часть моего алгоритма запускает тысячи операций поиска и замены, например s/(ome)?reg[3]x/blah$1/g, для многократного изменения данных. Поэтому мне нужно эффективное сопоставление шаблонов регулярных выражений (возможно, regex-tdfa?) , а также сплайсинг (данные [a: b] = newData), где необязательно (length(newData) == b-a+1)

  • Lazy ByteStrings может быть в порядке, но как насчет сращивания? Скрещивание ByteString - это линейное время O (dataSize/chunkSize) линейного времени (для поиска) плюс (возможно?) Накладные расходы для обслуживания блоков постоянного размера. (Может быть, неверно в отношении последней части); vs O (log (dataSize)) для FingerTree.

  • Мой тип данных "сдерживаемый" абстрактно представляет собой конечный алфавит. Он может быть представлен конкретно Char или Byte или Word8 или даже что-то вроде гипотетического Word4 (nibble). ** У меня есть связанный вопрос о том, как эффективно использовать newtype или data, чтобы мой код мог ссылаться на абстрактный алфавит, но скомпилированная программа все еще может быть эффективной. (Я должен опубликовать этот вопрос отдельно.)

  • Проблемы с производительностью. Возможно, Seq намного хуже, чем ByteString (по q значимому постоянному коэффициенту). В простых тестах, прочитав 7 МБ в строгом ByteString, а затем распечатав его на консольные пики при использовании реальной памяти 60 МБ (в соответствии с Windows Process Manager), но загружая это содержимое в Seq Char, а затем печать использует 400 МБ! (Я должен опубликовать этот вопрос отдельно, с информацией о кодах и профилировании.)

  • Проблемы с платформой. Я использую EclipseFP и платформу Haskell. У меня есть текст, установленный на моей машине, и я хотел попробовать, но моя среда Eclipse не может его найти. У меня возникают серьезные проблемы, когда я использую cabal install (несовместимые версии пакетов устанавливаются, путаница --user vs --global), поэтому я хочу придерживаться пакетов Platform, которые может найти EclipseFP. Я думаю, что Text переходит в следующую версию Platform, так что это будет хорошо.

  • Trifecta​​strong > . Я кратко увидел Trifecta, и это просто добавило мне в замешательство. (Почему у него есть собственные новые реализации общих структур данных, которые уже опубликованы? Они лучше? Слишком много почти идентичных опций!)

Отредактировано с более подробной информацией и улучшенными ссылками.

Этот вопрос стал большим.

Сводка @ehird является основной точкой приема. Веревку или дерево пальцев байтов или векторов плюс небольшой пользовательский моноид. В любом случае, мне нужно будет написать простую реализацию регулярного выражения, чтобы приклеить.

Учитывая всю эту информацию, я бы рекомендовал либо Веревку, либо здание ваша собственная структура с пакетом fingertree, на котором она основана (скорее чем Seq, чтобы вы могли правильно выполнять такие вещи, как длина Тип измеряемого типа - см. Моноиды и пальмовые деревья), с листом данные, помеченные в unboxed Vector. Последнее, конечно, больше работа, но позволяет вам оптимизировать специально для вашего прецедента. Так или иначе, определенно завершите его в абстрактном интерфейсе.

Я вернусь позже сегодня и разделился на новые вопросы. Я получу низкоуровневые технические вопросы, а затем вернусь к общему сравнению. Я изменю заголовок вопроса, чтобы лучше отразить мою реальную проблему. "Какие модули Haskell предоставляют или поддерживают операции манипуляции последовательностями, которые мне нужны эффективно?" Благодарим вас за чужие и другие ответчики.

4b9b3361

Ответ 1

Для остальной части этого ответа, я предполагаю, что вы на самом деле пытаетесь сохранить сырые байты, а не символы. Если вы хотите хранить символы, вам следует рассмотреть возможность использования text (эквивалент ByteString для текста в Юникоде) или написание собственного текста основанная на нем. Вы также можете использовать ByteString с Data.ByteString.UTF8 из utf8-string; Я думаю, что это может оказаться более эффективным, но гораздо менее полно, чем Text для текста в Юникоде.

Ну, связанный вами пакет веревок хранит эквивалент ByteString s, тогда как Seq является общим и может обрабатывать любые типы данных; первая, вероятно, будет более эффективной для хранения, ну, строк байтов.

Я подозреваю, что это та же самая важная древовидная структура, поскольку веревка реализует "кончики пальцев", а Seq - это 2-3 пальца; это зависит от (и, предположительно, использует) пакет fingertree, который по существу совпадает с Data.Sequence, но более общим. Вероятно, что веревка упаковывает данные в короткие ByteString s, что невозможно сделать с Seq (без прерывания операций, таких как length и т.д.).

В целом, веревка кажется лучшей структурой, если вы храните данные о байтовой строке и, как представляется, имеют фантастическую функциональность для "аннотирования" сегментов строки; однако последний раз он был обновлен в июле, а новая библиотека корректоров парсеров trifecta того же автора (впервые выпущенная в августе) содержит ее собственный набор канатных модулей, поэтому может быть неразумно создавать новый код на нем. Конечно, изменения, сделанные для trifecta, могут не иметь отношения к общему использованию, и, вероятно, было бы не так сложно разделить их как новую версию веревки; возможно, единственная причина, по которой они не были, состоит в том, что у trifecta уже есть тонна зависимостей:)

Но если вам нужен общий тип контейнера в любой момент вашей обработки (например, разбор байтов в последовательности более богатого представления) или хотите придерживаться того, что на платформе Haskell, тогда вам нужно будет использовать Seq.

Вы уверены, что ByteString или Text (так как вы упомянули персонажей) не подходят для того, что вы делаете? Они хранят поля смещения и длины, так что взятие подстроки не вызывает никакого копирования. Если ваши операции с вставкой не так редки, то это может сработать. Возможно, стоит рассмотреть и структуру на основе IntMap.


В ответ на ваш обновленный вопрос:

  • Регулярные выражения для пользовательских типов строк: Имейте в виду, что для использования существующей реализации регулярного выражения с "необычным" строковым типом вам нужно реализовать поддержку, чтобы приклеить ее к существующему коду regex-tdfa. Я не уверен, какова будет конечная производительность.
  • Склеивание с ленивым ByteString s: Обратите внимание, что lazy ByteString по умолчанию использует 64 KiB куска, и вы можете использовать куски как можно больше, используя fromChunks вручную. Но вы правы, пальцевое дерево, вероятно, лучше подходит; это просто больше работы, которая уже обрабатывается для вас с ленивыми ByteString s.
  • Конечный алфавит: ОК; Я бы предложил вам абстрагировать (с newtype) тип, представляющий последовательность этого алфавита. Таким образом, вы можете попробовать различные реализации, надеясь локализовать работу, которая должна быть выполнена, поэтому вы можете выбирать на основе реальных данных о производительности, а не догадки:) Конечно, все еще стоит авансовый расход на создание новой реализации. Что касается вашего дополнительного вопроса, newtype удаляются во время компиляции, поэтому newtype имеет такое же представление времени выполнения, как и тип, который он обертывает. Короче говоря, не волнуйтесь об обертывании вещей в newtype s.
  • Производительность Seq: Ну, это не удивительно. Seq Char полностью ленив и помещен в коробку и не будет "chunking" Char вместе, как Rope; он, вероятно, даже менее эффективен с точки зрения памяти, чем String. Что-то вроде Seq ByteString может работать намного лучше, но если ваши куски не имеют постоянного размера, вы потеряете возможность получить значимую длину и т.д., Не пройдя все это.
  • Проблемы с пакетом EclipseFP: Я бы не выбрал, какое представление использовать на основе простых проблем с инструментами; Я рекомендую задать новый вопрос.
  • Trifecta:Я не думаю, что trifecta имеет отношение к вашей проблеме; это просто написано тем же автором, что и веревка, поэтому он имеет отношение к продолжению развития веревки. Это просто библиотека парсера-анализатора, такая как Parsec, и она больше фокусируется на диагностике и т.д., А не на производительности, поэтому я не думаю, что она может заменить ваши регулярные выражения.

Что касается # 3, вместо ByteString вам может потребоваться unboxed Vector; Таким образом, вы можете использовать свой абстрактный алфавитный тип, а не взламывать вещи в интерфейс ByteString Word8.

Учитывая всю эту информацию, я бы рекомендовал либо Rope, либо создать свою собственную структуру с помощью fingertree пакета на основе ( а не Seq, чтобы вы могли корректно реализовывать такие вещи, как length, с типом класса Measured - см. Моноиды и пальцы деревьев), при этом листовые данные помещаются в распакованный Vector. Последнее, конечно, больше работает, но позволяет вам оптимизировать специально для вашего прецедента. В любом случае, определенно завершите его в абстрактном интерфейсе.

Кстати, регулярные выражения не так хорошо поддерживаются в экосистеме Хаскелла, как могли бы быть; возможно, стоит подумать о том, чтобы использовать что-то еще, если есть возможность сделать это. Но это зависит от конкретных деталей вашей программы, чтобы дать конкретную рекомендацию.