我有一组整数{1,2}。我想生成“ Transform#1,Transform#2”,其中每个元素都经过转换,然后用定界符累积结果。
最简单的方法是什么?我们在c ++中有“折叠”,“地图”吗?
我们不使用升压。
答案 0 :(得分:15)
您可以使用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
答案 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,
}
答案 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及更高版本中使用:
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)
答案 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";
}