头尾打印比检查结束或开始更有效吗?

时间:2014-03-04 07:02:05

标签: c++ list loops c++11 lambda

所以我有一份清单。我想打印以空格分隔的列表中的所有元素。

我头脑中的第一件事就是做,

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

假设此解决方案比原始条件版本更高效,性能更合理是否合理?

4 个答案:

答案 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 << ", ";
        }
    }
}

Live Example打印

  

1,2,3,4,5,6,6,7,8,9,6

即。在最后一个元素的末尾没有分隔符(我使用“,”作为分隔符,因为它比空格更容易发现,当然可以根据您的意愿调整)。

“中间检查”使得它与范围,whiledo-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;列表和列表元素的运算符。

  • '&lt;&lt;&lt;对于列表,将注意迭代所有元素
  • '&lt;&lt;&lt;对于每个元素,都要注意用正确数量的空格打印出来。

这会将原始代码缩减为:

cout << somelist;