составление нескольких предикатов для алгоритмов C ++ 11 - PullRequest
0 голосов
/ 05 октября 2018

Я читал похожие вопросы по stackoverflow, такие как this .Мой конкретный вариант использования кажется немного другим: вместо того, чтобы постоянно применять все предикаты, мне нужно по-разному выбирать и комбинировать их (&&, ||) в ожидании ввода пользователя: лучший пример, который я могу придумать, это unix find, где пользователь может:

# find all files on condition:
# size more than 100K, modified 7 days ago, from user group abc 
find /path/to/somewhere -type f -size +100K -mtime +7 -group abc

Предположим, у меня определены следующие предикаты:

bool size_equal_to() { ... }
bool size_greater_to() { ... }
bool size_less_than() { ... }
bool type_is_file() { ... }
bool type_is_dir() { ... }
bool mtime_plus() { ... }

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

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

То, что вы описываете, - это дерево предикатов, где листья - это ваш атом size_equal_to и т. Д., А узлы являются объединителями (&& или ||).

У вас есть что-то вроде

class Predicate
{
public:
    virtual ~Predicate();
    virtual bool operator()(Item &) = 0;
};

// child1 && child2 && ...
class Conjunction : public Predicate
{
    std::vector<std::unique_ptr<Predicate>> children;
public:
    bool operator()(Item & item)
    {
        for (auto & child : children)
            if (!(*child)(item)) 
                return false;
        return true;
    }
};

// child1 || child2 || ...
class Disjunction : public Predicate
{
    std::vector<std::unique_ptr<Predicate>> children;
public:
    bool operator()(Item & item)
    {
        for (auto & child : children)
            if ((*child)(item)) 
                return true;
        return false;
    }
};

// just inherit the operator() from a lambda
template <typename Lambda>
class Leaf : public Predicate, public Lambda 
{
    Leaf(Lambda lambda) : Lambda(lambda) {}
    using Lambda::operator();
};

Затем вам нужно будет записать сопоставление вашего пользовательского ввода с этими Predicate объектами, например, проанализировать каждый аргумент, привязать параметры к вашему size_equal_to или w / e, а затем добавить его кConjunction.

Если это не ввод командной строки, а графический интерфейс, вы, вероятно, захотите нарисовать его в виде дерева с узлами «И» и «ИЛИ».

0 голосов
/ 05 октября 2018

Ну, во-первых, если это похоже на find, вы ограничены IO, так что вы можете просто использовать флаги.Не нужно увлекаться.

Но теперь давайте предположим, что вы на самом деле ограничены вычислениями, и тестирование флагов является значительным:

Просто напишите шаблон-функцию, выполняющую все в зависимости от того, установлен ли флаг варгумент шаблона устанавливается, а затем сопоставляется с флагами в переменной с флагами в аргументе шаблона, например:

class Task {
    ... Whatever
public:
    template <std::size_t N>
    void operator()(std::integral_constant<std::size_t, N>) {
        if (N & 1) test_0();
        if (N & 2) test_1();
        if (N & 4) test_2();
        ...
    }
}

my_mux<1<<6>(n, the_task);

Конечно, вам также нужно my_mux():

#include <type_traits>
#include <stdexcept>
namespace detail {
    template <std::size_t N, class F>
    typename std::enable_if<!N>::type my_mux_helper(std::size_t n, F& f) {
        n == 0 ? f(std::integral_constant<std::size_t, N>()) : throw std::invalid_argument("n must be smaller than N");
    }
    template <std::size_t N, class F>
    typename std::enable_if<N>::type my_mux_helper(std::size_t n, F& f) {
        n == N ? f(std::integral_constant<std::size_t, N>()) : my_mux_helper<N - 1>(n, f);
    }
}

template <std::size_t N, class F>
void my_mux(std::size_t n, F f) {
    detail::my_mux_helper<N - 1>(n, f);
}

Посмотреть онлайн на coliru .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...