在C中循环TWICE最优雅的方法是什么?

时间:2010-10-19 19:05:11

标签: c loops iterator

很多时候我需要在for循环中做两件事。简单地说,我可以使用迭代器设置for循环并进行两次:

for (i = 0; i < 2; i++)
{
 // Do stuff
}

现在我有兴趣尽可能简单地做这个,也许没有初始化器或迭代器?有没有其他的,非常简单和优雅的方法来实现这个目标?

24 个答案:

答案 0 :(得分:59)

这很优雅,因为它看起来像一个三角形;和三角形都很优雅。

i = 0; 
here: dostuff(); 
i++; if ( i == 1 ) goto here;

答案 1 :(得分:35)

将其封装在一个函数中并调用它两次。

void do_stuff() {
  // Do Stuff
}

// .....

do_stuff();
do_stuff();

注意:如果在 stuff 逻辑中使用封闭函数的变量或参数,则可以将它们作为参数传递给extracted {{1功能。

答案 2 :(得分:30)

如果它只有两次,并且你想避免循环,只需写两次darn的东西

statement1;
statement1;  // (again)

答案 3 :(得分:16)

如果循环对你来说过于冗长,你也可以为它定义一个别名:

#define TWICE for (int _index = 0; _index < 2; _index++)

这会导致代码:

TWICE {
    // Do Stuff
}

// or

TWICE
    func();

我只建议使用这个宏,如果你必须经常这样做,我认为普通的for循环更具可读性。

答案 4 :(得分:9)

不幸的是,this不适用于C,但仅适用于C ++,但完全符合您的要求:

只需包含标题,您就可以编写如下内容:

10 times {
  // Do stuff
}

我也会尝试为C重写它。

答案 5 :(得分:8)

因此,经过一段时间后,这里有一种方法可以让您在纯C中编写以下内容:

2 times {
  do_something()
}

实施例

您必须将this little thing包含为简单的头文件(我总是将文件称为extension.h)。然后,您将能够以以下方式编写程序:

#include<stdio.h>
#include"extension.h"

int main(int argc, char** argv){
    3 times printf("Hello.\n");
    3 times printf("Score: 0 : %d\n", _);
    2 times {
        printf("Counting: ");
        9 times printf("%d ", _);
        printf("\n");
    }
    5 times {
        printf("Counting up to %d: ", _);
        _ times printf("%d ", _);
        printf("\n");
    }
    return 0;
}

特点:

  • 简单循环的简单表示法(在上面描述的样式中)
  • 计数器隐式存储在名为_的变量(简单的下划线)中。
  • 允许嵌套循环。

限制(以及如何(部分)规避它们):

  • 仅适用于一定数量的循环(当然是“ - 当然” - 合理,因为您只想将这样的东西用于“小”循环)。当前实现最多支持18次迭代(较高的值会导致未定义的行为)。可以通过更改数组_A的大小在头文件中进行调整。
  • 仅允许某个嵌套深度。当前实现支持嵌套深度10.可以通过重新定义宏_Y来调整。

说明:

您可以看到full (=de-obfuscated) source-code here。假设我们想要允许多达18个循环。

  • 检索上层迭代绑定:基本思想是拥有一个char的数组,这些数组最初都设置为0(这是数组counterarray)。如果我们发出呼叫,例如2 times {do_it;} times应将counterarray的第二个元素设置为1(即counterarray[2] = 1)。在C中,可以在这样的赋值中交换索引和数组名称,因此我们可以编写2[counterarray] = 1来实现相同的操作。这正是宏times作为第一步所做的事情。然后,我们可以稍后扫描数组counterarray,直到我们找到一个不为0的元素,但是1.相应的索引就是上面的迭代边界。它存储在变量searcher中。由于我们要支持嵌套,我们必须分别为每个嵌套深度存储上限,这由searchermax[depth]=searcher+1完成。
  • 调整当前嵌套深度如上所述,我们希望支持循环嵌套,因此我们必须跟踪当前嵌套深度(在变量depth中完成)。如果我们开始这样的循环,我们将它增加一。
  • 实际的计数器变量:我们有一个名为_的“变量”,它隐式地被赋予当前计数器。实际上,我们为每个嵌套深度存储一个计数器(全部存储在数组counter中。)然后,_只是另一个宏,它从该数组中检索当前嵌套深度的正确计数器。
  • 实际for循环:我们将for循环分为几部分:
    • 我们将当前嵌套深度的计数器初始化为0(由counter[depth] = 0完成)。
    • 迭代步骤是最复杂的部分:我们必须检查当前嵌套深度的循环是否已到达终点。如果是这样,我们会相应地更新嵌套深度。如果不是,我们必须将当前嵌套深度的计数器增加1.如果这是最后一次迭代,则变量lastloop为1,否则为0,我们相应地调整当前嵌套深度。这里的主要问题是我们必须将它写成一个表达式序列,所有表达式都用逗号分隔,这要求我们以非常直接的方式编写所有这些条件。
    • for循环的“递增步骤”仅包含一个赋值,它递增适当的计数器(即正确嵌套深度的counter的元素)并将此值赋给我们的“计数器变量”{ {1}}。

答案 6 :(得分:4)

abelenky说的是什么。

如果您的{ // Do stuff }是多行的,请将其设为函数,并调用该函数 - 两次。

答案 7 :(得分:3)

这个怎么样?

void DostuffFunction(){}

for (unsigned i = 0; i < 2; ++i, DostuffFunction());

此致 巴勃罗。

答案 8 :(得分:3)

另一次尝试:

for(i=2;i--;) /* Do stuff */

此解决方案有许多好处:

  • 最短的形式,我声称(13个字符)
  • 仍然可读
  • 包含初始化
  • 代码中可以看到重复次数(“2”)
  • 可用作身体内的切换(1或0),例如进行交替
  • 使用单指令,指令正文或函数调用
  • 灵活(不必仅用于“做两次”)
  • 符合Dijkstra标准; - )

来自评论:

for (i=2; i--; "Do stuff");

答案 9 :(得分:2)

假设C ++ 0x lambda支持:

template <typename T> void twice(T t)
{
    t();
    t();
}

twice([](){ /*insert code here*/ });

或者:

twice([]()
{ 
    /*insert code here*/ 
});

由于你想要C语言,这对你没有帮助。

答案 10 :(得分:2)

很多人建议两次写出代码,如果代码很短,这很好。但是,有一个代码块的大小,复制起来不方便,但不足以值得它自己的功能(特别是如果该功能需要过多的参数)。我自己正常运行循环'n'次的习惯是

  i = number_of_reps;
  do
  {
    ... whatever
  } while(--i);

在某种程度上,因为我经常为嵌入式系统编码,其中向上计数循环通常效率低下,并且在某种程度上因为很容易看到重复次数。两次运行有点尴尬,因为我的目标系统上最有效的编码

  bit rep_flag;

  rep_flag = 0;
  do
  {
    ...
  } while(rep_flag ^= 1); /* Note: if loop runs to completion, leaves rep_flag clear */

读得不太好。使用数字计数器表明,reps的数量可以任意改变,在许多情况下不是这种情况。不过,数字计数器可能是最好的选择。

答案 11 :(得分:2)

  

正如Edsger W. Dijkstra自己说的那样:“两个或更多,使用一个”。不需要更简单。

答案 12 :(得分:2)

如果你的编译器支持这个,只需将声明放在for语句中:

for (unsigned i = 0; i < 2; ++i)
{
 // Do stuff
}

这是优雅和高效的。现代编译器可以进行循环展开和所有这些东西,相信它们。如果您不信任它们,请检查汇编程序。

它对所有其他解决方案都有一点点优势,对于它所读取的每个人来说,“做两次”。

答案 13 :(得分:2)

使用功能:

func();
func();

或使用宏(不推荐):

#define DO_IT_TWICE(A) A; A

DO_IT_TWICE({ x+=cos(123); func(x); })

答案 14 :(得分:1)

如果没有预处理器/模板/复制技巧,这是最短的:

for(int i=2; i--; ) /*do stuff*/;

请注意,减量在开头就发生了一次,这就是为什么这将按照请求使用索引10精确循环两次。

或者你可以写

for(int i=2; i--; /*do stuff*/) ;

但这纯粹是品味的差异。

答案 15 :(得分:1)

好规则:三个或更多,

我想我在Code Complete中读到了这一点,但我可能错了。因此,在您的情况下,您不需要for循环。

答案 16 :(得分:0)

贴近你的榜样,优雅高效:

for (i = 2; i; --i)
{
    /* Do stuff */
}

这就是我推荐这种方法的原因:

  • 它将迭代器初始化为迭代次数,这很直观。
  • 它使用过增量递减,使得环路测试表达式是一个比较为零(第“i;”可以被解释为“?为i真”,这在C表示“为i非零”),其可以在某些架构上进行更好的优化。
  • 它使用相对于后递减在出于同样的原因计数表达(可以优化更好)预先递减。
  • 它使用一个for循环,而不是DO /同时或goto或XOR或开关或宏或任何其它特技方式,因为可读性和可维护性比聪明的黑客更优雅和重要的。
  • 它不需要您复制“Do stuff”的代码,以便您可以避免循环。重复的代码是令人厌恶和维护的噩梦。

如果“Do stuff”很长,请将其移动到函数中,并授予编译器内联权限,如果有益的话。然后从for循环中调用该函数。

答案 17 :(得分:0)

我喜欢Chris Case的解决方案(在此处),但是C语言没有默认参数。

我的解决方案:

bool cy = false;
do {
    // Do stuff twice
} while (cy = !cy);

如果需要,可以通过检查布尔变量(可能是三元运算符)在两个循环中做不同的事情。

答案 18 :(得分:0)

什么是优雅?你怎么测量它?有人付钱给你优雅吗?如果是这样,他们如何确定从美元到优雅的转换?

当我问自己时,“应该怎么写”,我考虑付钱给我的人的优先事项。如果我付钱写快速代码,control-c,control-v,就完成了。如果我付钱快速编写代码,那么......同样的事情。如果我付费编写代码占据屏幕上最小的空间,我会缩短雇主的存量。

答案 19 :(得分:0)

首先,使用评论

/* Do the following stuff twice */

然后,
1)使用for循环
2)写两次声明,或者 3)写一个函数并调用该函数两次 不要使用宏,如前所述,宏是邪恶的。

(我的答案几乎是一个三角形)

答案 20 :(得分:0)

//dostuff
  stuff;
//dostuff (Attention I am doing the same stuff for the :**2nd** time)
  stuff;

答案 21 :(得分:0)

跳转指令非常慢,所以如果你一个接一个地写行,它会比写一个循环更快。但现代编译器非常非常智能,优化程度很高(当然,如果允许的话)。如果你打开了编译器的优化,你就不在乎了,你写它 - 用循环或不用(:

编辑http://en.wikipedia.org/wiki/compiler_optimizations只需看看(:

答案 22 :(得分:0)

如果您正在做的事情有点复杂,请将其包装在函数中并调用该函数两次? (这取决于do stuff代码依赖的局部变量数量。)

您可以执行类似

的操作
void do_stuff(int i){
    // do stuff
}

do_stuff(0);
do_stuff(1);

但如果你正在研究一大堆局部变量,这可能会变得非常难看。

答案 23 :(得分:-1)

void loopTwice (bool first = true)
{
    // Recursion is your friend
    if (first) {loopTwice(false);}

    // Do Stuff
    ...
}

我确信这是一种更优雅的方式,但这很容易阅读,而且非常简单。甚至可能有一种消除bool参数的方法,但这是我在20秒内提出的。