我有一组整数{1,2}。我想生成“Transform#1,Transform#2”,其中每个元素都被转换,然后结果用分隔符累积。
最简单的方法是什么?我们在c ++中有“折叠”,“地图”吗?
我们不使用提升。
你可以使用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
您可能想要使用Range Adaptors。 Boost已经拥有它们,并且它们正在达到C ++ 20的标准。
看看boost::adaptors::transformed
示例here。另外,请查看reference以更好地了解适配器支持的操作。
最后,您可以实现更清晰的代码,并且性能差异可以忽略不计(与其他语言不同,使用这种编程风格会导致高性能成本)。
如果您可以站在尾随分隔符,则以下函数可以将任何可迭代的数据范围{ 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,
}
你可以使用简单的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
假设我正确理解了这个问题,以下简单的实现看起来也很简单。此函数适用于C ++ 11及更高版本:
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
。这仍然很简单:
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)
- 尺寸为16和32的性能测试。(DEMO)
除非您有其他要求保留中间转换列表,否则存储它是次优的。您可以直接致电qazxsw poi并进行两项操作:
std::accumulate
如果您有使用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