Алгоритм STL для расщепления вектора на несколько меньших по лямбде - PullRequest
1 голос
/ 07 марта 2019

Допустим, у меня есть вектор, содержащий структуру с членом, описывающим его целевой вектор.

struct Foo
{
  int target;
  static const int A = 0;
  static const int B = 1;
  static const int C = 2;
};

std::vector<Foo> elements;

std::vector<Foo> As;
std::vector<Foo> Bs;
std::vector<Foo> Cs;
std::vector<Foo> others;

Теперь я хочу переместить каждый Foo в один из четырех других векторов, основываясь на значении Target.

Например

auto elements = std::vector<Foo>{ {Foo::A}, {Foo::A}, {Foo::B} };

В результате получится два элемента в As, один в Bs и ни один в Cs или others. Elements впоследствии должно быть пустым.

Я мог бы сделать это сам, но мне интересно, есть ли алгоритм STL, который я мог бы использовать для своей работы.

1 Ответ

1 голос
/ 07 марта 2019

Стандартные алгоритмы обычно не работают с несколькими выходными адресатами, поэтому здесь сложно найти подходящее решение, если вы хотите абстрагировать контейнеры назначения с помощью итераторов вывода.Что может быть ближе всего - это std::copy_if.Это может выглядеть следующим образом:

// Help predicate creation:
auto pred = [](int target){ return [target](const Foo& f){ return f.target == target; }; };

std::copy_if(elements.begin(), elements.end(), std::back_inserter(As), pred(Foo::A));
std::copy_if(elements.begin(), elements.end(), std::back_inserter(Bs), pred(Foo::B));
std::copy_if(elements.begin(), elements.end(), std::back_inserter(Cs), pred(Foo::C));
std::copy_if(elements.begin(), elements.end(), std::back_inserter(others),
    [](const Foo& f){ return false; /* TODO */ });

elements.clear();

Если копирование обходится дороже, чем создание ходов, вы должны передать алгоритму std::make_move_iterator(elements.begin()) и то же самое для elements.end().Проблема здесь в том, что это не масштабируется.std::copy_if линейно пересекает входной диапазон, и вышеописанное должно сделать это четыре раза.Один обход может быть получен, например, следующим образом.

auto doTheWork = [&As, &Bs, &Cs, &others](const Foo& foo) {
   if (foo.target == Foo::A)
      As.push_back(foo);
   else if (foo.target == Foo::B)
      Bs.push_back(foo);
   else if (foo.target == Foo::C)
      Cs.push_back(foo);
   else
      others.push_back(foo);
};

std::for_each(elements.begin(), elements.end(), doTheWork);

В этом сценарии мы по крайней мере использовали стандартный алгоритм, но сместили логику в довольно некрасивую лямбду.Обратите внимание, что лямбда, указанная выше, всегда будет копировать свои аргументы, для корректной работы с std::move_iterator s необходимо несколько настроек. Иногда старый добрый диапазон, основанный на цикле, является наиболее читаемым решением.

...