Дизайн для настраиваемого строкового фильтра - PullRequest
3 голосов
/ 14 мая 2011

Предположим, у меня есть тонны имен файлов в my_dir/my_subdir, отформатированных некоторым образом:

data11_7TeV.00179691.physics_Egamma.merge.NTUP_PHOTON.f360_m796_p541_tid319627_00
data11_7TeV.00180400.physics_Egamma.merge.NTUP_PHOTON.f369_m812_p541_tid334757_00
data11_7TeV.00178109.physics_Egamma.merge.D2AOD_DIPHO.f351_m765_p539_p540_tid312017_00

Например, data11_7TeV - это data_type, 00179691 номер прогона, NTUP_PHOTON формат данных.

Я хочу написать интерфейс, чтобы сделать что-то вроде этого:

dataset = DataManager("my_dir/my_subdir").filter_type("data11_7TeV").filter_run("> 00179691").filter_tag("m = 796");                     
// don't to the filtering, be lazy
cout << dataset.count();                          // count is an action, do the filtering
vector<string> dataset_list = dataset.get_list(); // don't repeat the filtering
dataset.save_filter("file.txt", "ALIAS");         // save the filter (not the filenames), for example save the regex
dataset2 = DataManagerAlias("file.txt", "ALIAS"); // get the saved filter
cout << dataset2.filter_tag("p = 123").count();

Я хочу ленивое поведение, например, никакая реальная фильтрация не должна выполняться перед такими действиями, какcount или get_list.Я не хочу повторять фильтрацию, если она уже сделана.Я только что-то узнал о шаблоне проектирования и думаю, что могу использовать:

  • абстрактный базовый класс AbstractFilter, который реализует filter* методы
  • factory чтобы решить из вызываемого метода, который декоратор использует
  • каждый раз, когда я вызываю filter* метод, я возвращаю декорированный класс, например:

AbstractFilter::filter_run(string arg) {
    decorator = factory.get_decorator_run(arg);  // if arg is "> 00179691" returns FilterRunGreater(00179691)
    return decorator(this);
}

  • прокси , который создает регулярное выражение для фильтрации имен файлов, но не выполняет фильтрацию

Я также учусьjQuery и я использую аналогичный механизм цепочки.

Может кто-нибудь дать мне несколько советов?Есть ли место, где объясняется такой дизайн?Дизайн должен быть очень гибким, в частности, для обработки нового формата в именах файлов.

Ответы [ 4 ]

3 голосов
/ 27 мая 2011

Я полагаю, что вы чрезмерно усложняете аспект шаблона проектирования и скрываете основные проблемы сопоставления / индексации.Можно ожидать, что получение полного списка каталогов с диска будет на несколько порядков дороже, чем фильтрация имен файлов в оперативной памяти, которые он возвращает, и первое должно быть завершено, прежде чем вы сможете выполнить count() или get_list() для любого dataset (хотя вы могли бы придумать несколько более ленивых операций итератора над dataset).

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

Но, допустим, вы прочитали все соответствующие записи каталога в массив A.

Теперь для фильтрацииПохоже, что ваши требования могут быть выполнены с использованием std::multimap find(), lower_bound() и upper_bound().Наиболее общий способ решения этой проблемы состоит в том, чтобы иметь отдельные мультикарты для типа данных, номера прогона, формата данных, значения p, значения m, tid и т. Д., Которые сопоставляются со списком индексов в A. Затем вы можетеиспользуйте существующие алгоритмы STL, чтобы найти индексы, которые являются общими для результатов ваших отдельных фильтров.

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

  • , если вы знаете, что всегда будет использоваться определенный фильтр, и немедленно сокращает потенциальные совпадения до управляемого числа (например, <~ 100), тогда вы могли бы сначала использовать его и прибегнуть кbrute force ищет последующую фильтрацию. </li>

Другая возможность состоит в том, чтобы извлечь свойства отдельных имен файлов в структуру: std::string data_type; std::vector<int> p; и т. д., а затем написать оценщик выражений, поддерживающий предикаты типа «p включает 924и data_type == '' XYZ '", хотя сам по себе он подходит для сравнений грубой силы, а не для более быстрого сопоставления на основе индексов.

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

1 голос
/ 31 мая 2011

Прежде всего, я думаю, что ваш дизайн довольно умный и хорошо подходит для поведения, которое вы пытаетесь смоделировать.

В любом случае, я понимаю, что вы пытаетесь создать некий «предметно-ориентированный язык», в котором вы можете связать «глаголы» (различные методы фильтрации), представляющие действия или соединяющие «сущности» (где изменчивость представлены различными форматами именования, которые могут существовать, хотя вы ничего не говорите об этом).

В этом отношении очень интересное обсуждение можно найти в книге Мартина Флоулера " Специфичные для домена языки ". Просто чтобы дать вам представление о том, что это такое, здесь , вы можете найти интересную дискуссию о паттерне "связывание методов", определенном как:

«Методы make-модификаторов возвращают хост-объект, чтобы в одном выражении можно было вызывать несколько модификаторов».

Как видите, этот шаблон описывает тот самый механизм цепочки, который вы используете в своем дизайне.

Здесь у вас есть список всех шаблонов, которые были найдены интересными при определении таких DSL. Опять же, вы легко найдете там несколько специализированных шаблонов, которые вы также подразумеваете в своем дизайне или описываете как более общие шаблоны (например, декоратор). Вот некоторые из них: лексер таблиц регулярных выражений, цепочка методов, построитель выражений и т. Д., И многое другое, что может помочь вам более точно определить свой дизайн.

В целом, я мог бы добавить свое зерно соли, сказав, что я вижу место для шаблона "командный процессор" в вашей спецификации, но я довольно уверен, что, развернув мощные абстракции, которые предлагает Фаулер, вы сможете придумать гораздо более конкретный и точный дизайн, охватывающий аспект проблемы, который сейчас просто скрывается «общностью» набора паттернов GoF.

Это правда, что это может быть "излишним" для такой проблемы, как та, которую вы описываете, но как упражнение в дизайне, ориентированном на шаблоны, оно может быть очень проницательным.

1 голос
/ 24 мая 2011

Я бы использовал шаблон стратегии.Ваш DataManager создает тип DataSet, а для DataSet назначен FilteringPolicy.По умолчанию может быть NullFilteringPolicy, что означает отсутствие фильтров.Если функция-член DataSet filter_type (строка t) вызывается, она заменяет класс политики фильтра новым.Новый может быть построен на заводе с помощью параметра filter_type.Такие методы, как filter_run (), могут использоваться для добавления условий фильтрации в FilterPolicy.В случае NullFilterPolicy это просто нет операций.Это кажется мне прямолинейным, я надеюсь, что это поможет.

РЕДАКТИРОВАТЬ: Для решения цепочки методов вам просто нужно вернуть * this;например, вернуть ссылку на класс DataSet.Это означает, что вы можете объединить методы DataSet вместе.Это то, что библиотеки iostream c ++ делают, когда вы реализуете operator >> или operator <<. </p>

0 голосов
/ 15 мая 2011

Я бы предложил начать с библиотеки повышающих итераторов - например, итератор фильтра .

(И, конечно же, boost включает в себя очень хорошую библиотеку регулярных выражений.)

...