所以我有一份清单。我想打印以空格分隔的列表中的所有元素。
我头脑中的第一件事就是做,
for (auto& ele : somelist)
{
cout << ele << ' ';
}
,或者
for (auto& ele : somelist)
{
cout << ' ' << ele;
}
问题在于引入了额外的空间。
然后,一种处理额外空间的方法是使用条件。
for (int idx{}; idx < somelist.size(); ++idx)
{
if (idx == 0)
cout << somelist[idx];
else
cout << ' ' << somelist[idx];
}
,或者
for (int idx{}; idx < somelist.size(); ++idx)
{
if (idx == somelist.size() - 1)
cout << somelist[idx];
else
cout << somelist[idx] << ' ';
}
但是,只有一次困扰我的条件才会成真。我想出了一种方法,使用lambdas打印一个列表来管理是否插入了额外的空格而不检查每次迭代。
#include <iostream>
#include <functional>
int main(int argc, char *argv[])
{
auto printHead = [] (int num)
{
std::cout << num;
};
auto printTail = [] (int num)
{
std::cout << ' ' << num;
};
// Need explicit type for lambda if its going to be captured
std::function<void(int)> print = [&printHead, &printTail, &print] (int num)
{
printHead(num);
print = printTail;
};
for (auto& element : {1,2,3,4,5,6,6,7,8,9,6})
{
print(element);
}
return 0;
}
假设此解决方案比原始条件版本更高效,性能更合理是否合理?
答案 0 :(得分:3)
您可以使用“ Loop and a Half ”构造(我不确定我是否计算与@Cornstalks相同的指令,但gcc -S -o -
给出153行汇编) :
#include <iostream>
#include <vector>
int main()
{
auto somelist = std::vector<int>{1,2,3,4,5,6,6,7,8,9,6};
auto first = begin(somelist), last = end(somelist);
if (first != last) { // initial check
while (true) {
std::cout << *first++;
if (first == last) break; // check in the middle
std::cout << ", ";
}
}
}
1,2,3,4,5,6,6,7,8,9,6
即。在最后一个元素的末尾没有分隔符(我使用“,”作为分隔符,因为它比空格更容易发现,当然可以根据您的意愿调整)。
“中间检查”使得它与范围,while
或do-while
循环不同。它还将lambda分成两个std::for_each
,这表明了一个非常通用的版本,它将std::for_each
概括为一个循环,取两个lambda,一个在检查之前,一个在检查之后:
template<class It, class UnaryOp1, class UnaryOp2>
void loop_and_a_half(It first, It last, UnaryOp1 op1, UnaryOp2 op2)
{
if (first == last) return;
while (true) {
op1(*first++);
if (first == last) break;
op2(*first);
}
}
可以像这样调用(使用C ++ 14泛型lambdas):
loop_and_a_half(
begin(somelist), end(somelist),
[](auto e) { std::cout << e; },
[](auto) { std::cout << ", "; }
);
Live Example打印相同的输出。
答案 1 :(得分:2)
如何将条件放在循环之外?
if (!somelist.empty()) std::cout << somelist[0];
for (int i = 1; i < somelist.size(); ++i)
{
std::cout << ' ' << somelist[i];
}
修改:我没有回答实际问题。让我们比较一下:
方法1(~243条说明):
std::vector<int> somelist = {1,2,3,4,5,6,6,7,8,9,6};
for (int idx{}; idx < somelist.size(); ++idx)
{
if (idx == 0)
std::cout << somelist[idx];
else
std::cout << ' ' << somelist[idx];
}
方法2(~555条指令):
auto printHead = [] (int num)
{
std::cout << num;
};
auto printTail = [] (int num)
{
std::cout << ' ' << num;
};
// Need explicit type for lambda if its going to be captured
std::function<void(int)> print = [&printHead, &printTail, &print] (int num)
{
printHead(num);
print = printTail;
};
std::vector<int> somelist = {1,2,3,4,5,6,6,7,8,9,6};
for (auto& element : somelist)
{
print(element);
}
方法3(~240指令):
std::vector<int> somelist = {1,2,3,4,5,6,6,7,8,9,6};
if (!somelist.empty()) std::cout << somelist[0];
for (int i = 1; i < somelist.size(); ++i)
{
std::cout << ' ' << somelist[i];
}
这些都是在OS X上用clang ++ 3.3编译的,-Ofast -flto
。虽然处理器是复杂的野兽,但很难说哪个版本最快(跨所有CPU),我认为方法#3可能最快(基于指令计数),接着是关闭方法#1,然后是方法#2。看起来您提出的方法会阻止大量的编译器优化。
答案 2 :(得分:2)
如何更简单的解决方案不涉及std::function
?
char space = '\0';
for (auto& ele : somelist)
{
cout << space << ele;
space = ' ';
}
或者您可以使用迭代器
auto first = somelist.cbegin();
auto last = somelist.cend();
if(first != last) {
cout << *first++;
}
for(; first != last; ++first) {
cout << ' ' << *first;
}
std::function
使用类型擦除(很可能通过虚函数)将调用分派给目标函数;这将涉及一些开销。要确定它是否比其他示例更快/更慢,您只需要测量它。
答案 3 :(得分:0)
为什么不实施'&lt;&lt;&lt;列表和列表元素的运算符。
这会将原始代码缩减为:
cout << somelist;