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

时间:2019-02-04 22:53:50

标签: c++ algorithm c++11 std c++17

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

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

我们不使用升压。

7 个答案:

答案 0 :(得分: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

答案 1 :(得分:7)

您可能要使用Range Adaptors。 Boost已经有了它们,并且它们已经成为C ++ 20的标准。

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

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

答案 2 :(得分:3)

如果您可以使用尾随分隔符,则以下函数可以将数据{ 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();
}

通过transforming将范围中的每个元素转换为stream iterator

用法

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 :(得分: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

答案 4 :(得分: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

答案 5 :(得分:1)

除非您有其他要求保留中间转换列表,否则存储它不是最佳选择。您只需致电std::string str; std::getline(std::cin, str); for (const auto & element : str | boost::adaptors::indexed(1)) { std::cout << element.index() << " : " << element.value() << std::endl; } 即可即时执行这两项操作:

std::accumulate

答案 6 :(得分:1)

如果您愿意使用C ++ 17,那么可以使用standard library algorithm来满足您的需求。这是一个示例:

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