如何对每个元素执行转换并将结果附加到c ++中?

问题描述 投票:6回答:7

我有一组整数{1,2}。我想生成“Transform#1,Transform#2”,其中每个元素都被转换,然后结果用分隔符累积。

最简单的方法是什么?我们在c ++中有“折叠”,“地图”吗?

我们不使用提升。

c++ algorithm c++11 std c++17
7个回答
15
投票

你可以使用std::transformstd::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


7
投票

您可能想要使用Range Adaptors。 Boost已经拥有它们,并且它们正在达到C ++ 20的标准。

看看boost::adaptors::transformed示例here。另外,请查看reference以更好地了解适配器支持的操作。

最后,您可以实现更清晰的代码,并且性能差异可以忽略不计(与其他语言不同,使用这种编程风格会导致高性能成本)。


3
投票

如果您可以站在尾随分隔符,则以下函数可以将任何可迭代的数据范围{ X, ..., Z }转换为字符串"<tag>X<sep>...<sep><tag>Z<sep>"

Code

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();
}

它的工作原理是transforming从范围内的每个元素到stream iterator

Usage

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, 
}

Live demo


3
投票

你可以使用简单的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


3
投票

假设我正确理解了这个问题,以下简单的实现看起来也很简单。此函数适用于C ++ 11及更高版本:

DEMO with 5 test cases

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表示数字字符的最小长度。我还在for循环中删除了delimiter+tag。这仍然很简单:

DEMO with 5 test cases

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;
}

我还测试了这些功能的性能和一些建议的答案如下。这些测试由gcc-8.2,C ++ 17和O3优化中的Quick C ++ Benchmark完成。由于std::transform_reduce在Quick C ++ Benchmark中仍然不可用,我还没有测试过。以上concatenate_fast至少在这些情况下表现出最佳表现,concatenate排名第二。

最后,就个人而言,考虑到可读性和性能的平衡,我想提出上述concatenate作为解决方案:

- 尺寸为2和8的性能测试。(DEMO

enter image description here

- 尺寸为16和32的性能测试。(DEMO

enter image description here


1
投票

除非您有其他要求保留中间转换列表,否则存储它是次优的。您可以直接致电qazxsw poi并进行两项操作:

std::accumulate

1
投票

如果您有使用C ++ 17的奢侈品,那么有一个#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; } 可以满足您的需求。这是一个例子:

standard library algorithm
© www.soinside.com 2019 - 2024. All rights reserved.