Как выполнить преобразование для каждого элемента и добавить результат в C ++? - PullRequest
0 голосов
/ 05 февраля 2019

У меня есть набор целых чисел {1,2}.Я хочу создать «Преобразование № 1, Преобразование № 2», где каждый элемент преобразуется, а затем результат накапливается с разделителем.

Какой самый простой способ сделать это?Есть ли у нас "сгибы", "карты" в c ++?

Мы не используем boost.

Ответы [ 7 ]

0 голосов
/ 10 февраля 2019

Если у вас есть возможность использовать C ++ 17, есть стандартный библиотечный алгоритм , который делает именно то, что вам нужно.Вот пример:

#include <iterator>
#include <iostream>
#include <numeric>
#include <string>

int main()
{
    auto input = {1, 2, 3};

    std::cout << std::transform_reduce(
        std::cbegin(input), std::cend(input),
        std::string("Result:"),
        [](const std::string & left, const std::string & right) { return left + " " + right; },
        [](int value) { return "Transform#" + std::to_string(value); }
    ) << "\n";
}
0 голосов
/ 10 февраля 2019

Предполагая, что я правильно понимаю проблему, следующая простая реализация также выглядит очень простой и легкой.Эта функция работает в C ++ 11 и более поздних версиях:

DEMO с 5 тестовыми наборами

std::string concatenate(
    const std::vector<int>& indecies, 
    const std::string& delimiter = ", ",
    const std::string& tag = "Transform#")
{
    if(indecies.empty()){
        return "";
    }

    std::string s(tag + std::to_string(indecies[0]));

    for(auto it = indecies.begin()+1; it != indecies.cend(); ++it){
        s += (delimiter + tag + std::to_string(*it));
    }

    return s;
}

(Кстати, как для этой функцииconcatenate, если indecies пусто, возвращаемое значение также является пустой строкой, а не исключениями (единица AndreasDM) или UB (единица Everlight). И если indecies имеет только один элемент, например indecies={1},тогда результат будет "Transform#1”, а не "Transform#1, ” (один из YSC) или ", Transform#1” (один из sakra). Они отличаются от других ответов, и эта функция будет более простой, если эта обработка будет удалена.)


Несмотря на то, что производительность не может быть основной, указанную выше функцию можно слегка оптимизировать, предварительно сохранив минимальную емкость для сохранения результирующей строки на std::basic_string::reserve следующим образом.Здесь +1 в *.size()+1 означает минимальную длину числового символа.Я также удалил delimiter+tag в цикле for.Это все еще выглядит просто:

DEMO с 5 тестовыми примерами

std::string concatenate_fast(
    const std::vector<int>& indecies, 
    std::string delimiter = ", ",
    const std::string& tag = "Transform#")
{
    if(indecies.empty()){
        return "";
    }

    std::string s(tag + std::to_string(indecies[0]));
    delimiter += tag;
    s.reserve((tag.size()+1) + (indecies.size()-1)*(delimiter.size()+1));

    for(auto it = indecies.begin()+1; it != indecies.cend(); ++it){
        s += (delimiter + std::to_string(*it));
    }

    return s;
}

Я также проверил производительность этих функций и некоторые предлагаемые ответыследующее.Эти тесты выполняются с помощью Quick C ++ Benchmark в рамках оптимизации gcc-8.2, C ++ 17 и O3.Поскольку std::transform_reduce по-прежнему недоступен в Quick C ++ Benchmark, я не проверял его.Приведенный выше concatenate_fast показывает лучшую производительность, по крайней мере, в этих случаях, а concatenate - второй.

Наконец, лично, принимая во внимание баланс читабельности и производительности, я хотел бы предложитьвыше concatenate как решение:

- Тест производительности с размерами 2 и 8. ( DEMO )

enter image description here

- Тест производительности с размерами 16 и 32. ( DEMO )

enter image description here

0 голосов
/ 10 февраля 2019

Вы можете выполнить фолд, используя просто std::accumulate

#include <set>
#include <string>
#include <iostream>
#include <numeric>

int main()
{
    auto transformation = [](int number) { return "Transform#" + std::to_string(number); };
    auto transform_and_fold = [&transformation](std::string init, int number) { return std::move(init) + ", " + transformation(number); };

    std::set<int> numbers{1, 2};
    std::cout << std::accumulate(std::next(numbers.begin()), numbers.end(), transformation(*numbers.begin()), transform_and_fold);
}

Выходы

Transform#1, Transform#2

0 голосов
/ 09 февраля 2019

Если у вас нет других требований для сохранения промежуточного преобразованного списка, его хранение является неоптимальным.Вы можете просто позвонить std::accumulate и выполнить обе операции на лету:

#include <cstdio>
#include <iterator>
#include <numeric>

int main ( )
{
    int const input [] = { 1, 2, 3, 4, 5, 6 };

    // computes sum of squares
    auto const add_square = [] ( int x, int y ) { return x + y * y; };
    int result = std::accumulate
        ( std::cbegin (input)
        , std::cend (input)
        , 0
        , add_square
        );

    std::printf ( "\n%i\n", result );

    return 0;
}
0 голосов
/ 07 февраля 2019

Если вы можете использовать конечный разделитель, следующая функция может преобразовать любой итеративный диапазон данных { X, ..., Z } в строку "<tag>X<sep>...<sep><tag>Z<sep>".

Код

template <class InputIt>
std::string f(InputIt begin, InputIt end, std::string_view separator = ", ", std::string_view tag = "Transform#")
{
    std::stringstream output;
    std::transform(begin, end,
        std::ostream_iterator<std::string>(output, separator.data()),
        [tag](auto const& element){ return std::string{tag} + std::to_string(element); }
    );
    return output.str();
}

Работает по преобразование каждого элемента из диапазона в потоковый итератор .

Использование

int main()
{
    std::set<int> const data{1, 2, 3}; // works with vector, string, list, C-arrays, etc.
    std::cout << f(begin(data), end(data)) << '\n';
    // prints Transform#1, Transform#2, Transform#3, 
}

Живая демонстрация

0 голосов
/ 05 февраля 2019

Вы можете использовать std::transform и std::accumulate

int main()
{
  std::vector<int>         v1 {1,2,3};
  std::vector<std::string> v2;

  std::transform(begin(v1), end(v1), std::back_inserter(v2), [](auto const& i) {
    return std::string("Transform#") + std::to_string(i);
  });

  std::string s = std::accumulate(std::next(begin(v2)), end(v2), v2.at(0), [](auto const& a, auto const& b) {
    return a + ", " + b;
  });

  std::cout << s;
}

отпечатки Transform#1, Transform#2, Transform#3

0 голосов
/ 05 февраля 2019

Возможно, вы захотите использовать Адаптеры дальности .Boost уже имеет их, и они приходят к стандарту с C ++ 20.

Взгляните на boost::adaptors::transformed пример здесь .Кроме того, ознакомьтесь с справкой , чтобы получить более полное представление о том, какие операции поддерживаются адаптерами.

В конце вы можете добиться более чистого кода, а разница в производительности незначительна (в отличие отнекоторые другие языки, где использование этого стиля программирования сопряжено с большими затратами на производительность).

...