今天进行编码时,我遇到了C#中的以下循环:

for(int x = 0; x < max_x_index; x++){
    for(int y = 0; y < max_y_index; y++, count++){
        some2DarrayRepresentedBy1D[count] = ...;
    }
}


这是否会被视为不良做法?以下是实现相同结果的更好方法:

for(int x = 0; x < max_x_index; x++){
    for(int y = 0; y < max_y_index; y++){
        some2DarrayRepresentedBy1D[count] = ...;
        count++;
    }
}


例如,允许您同时进行代码审查还是为第一个示例标记?晦涩吗?

评论

我看到这个问题同时被标记为“ C”和“ Java”。两种语言中的惯用语代码是不同的,因此最佳答案可能在不同的语言中也有所不同-例如,rolfl的some2Darray [count ++]在C语言中很常见,但我在Java中却很少见。

啊,我知道不是你,但是@ 200_success添加了标签...

@Izkata some2Darray [count ++]在Java中与在C中一样有效。没有理由或证据表明它在Java中不那么流行。

@ 200_success我从没说过有效性,我知道这对两种语言都是有效的。但是习惯用法并不总是跨语言的。

嗯,当然可以。谢谢@afuzzyllama。

#1 楼

我同意OP的观点,因为第二个代码块将是最好的方法。

原因-可读性。在我看来,在for循环语句中递增的变量仅应用于计数我们循环的次数。 count可以在循环之前设置,也可以在几个不同的循环中递增/递减。但是无论哪种情况,count都不用于跟踪当前循环的进度-在这种情况下,y就是这样。

因此,出于可读性考虑,我认为这是最佳做法仅在for循环行本身中增加用于循环通过for循环的变量。

#2 楼

在到目前为止的所有答案中,我认为Charlie74与我所做的推理最接近。

for循环包括三个部分:

for (initialization; termination; increment) {
    statement(s)
}


我认为只有附加终止的东西才应该是增量的一部分。换句话说,如果该值不属于终止条件,那么它就不应该是增量的一部分。 br />
在这种情况下,计数不影响终止条件,因此它不属于增量部分。.

对于它的价值,有一个我仍然会做一些不同的事情:


第二个示例中您有错别字....(在x中缺少x <,而缺少y <)[此问题已解决现在。]

我将把count ++作为数组分配的一部分(我认为它属于该数组):

for(int x = 0; x < max_x_index; x++){
    for(int y = 0; y < max_y_index; y++){ 
        some2Darray[count++] = ...;
    }
}



在这种情况下讨论++countcount++


是否是其中一种情况,如果将i++更改为++i,它将更改数组中分配的内容?您能解释一下...吗?


这里....是的,这是前增量或后增量差异显着的情况之一。 Wikipedia对此进行了介绍。

预递增运算符++count递增count变量。后递增运算符count++也会递增计数变量。区别在于,尽管++countcount++都是表达式,但表达式的结果值却不同。

重要的是要了解count++是一个表达式的含义。表达式是产生值的东西。在上下文array[count] = 1中,count是一个表达式,它是带有值的东西。相反,该表达式可以是常量(array[0]),函数(array[getNextIndex()]),甚至可以是由子表达式和运算符组成的更复杂的表达式(array[5 - 3])。因此,count++是一个表达式。同样,它修改了count变量中的值。类似地,++count是一个表达式,它也修改了count的值。它们之间的区别不是它们对count的作用,而是表达式的值。 />
使用count++时,表达式的值就是count递增之前的值。

使用++count时,表达式的值就是count递增之后的值。

因此,在上面的示例中,比较了两种不同的情况:

int count = 0;
array[count++] = 1;


vs.

int count = 0;
array[++count] = 1;


在第一个示例中,表达式count++的值为0(增量之前的值),因此,索引0 o处的值如果将array设置为1。即它等效于:

int count = 0;
array[count] = 1;
count = count +1;


在第二个示例中,表达式++count的值为1(递增后的值),结果,值为将1的索引array设置为1。即它等效于:

int count = 0;
count = count + 1;
array[count] = 1;


很明显,通过使用++count,我们将开始在索引1而不是0处添加数据,并且存在出局的风险。到达数据末尾时的边界操作。

在将数据添加到数组的情况下(例如此问题的处理方式),将计数器设置为要使用的“下一个”插槽是有意义的,然后进行后递增操作,以便“使用”下一个插槽,然后增加它,使其再次指向下一个插槽。

这样做的好处是,最后,count也是您在阵列中插槽的数量

烦躁不安的是,出现了另一个问题(在数组中找到所有具有最大值的索引),其中一个潜在的答案很好地说明了这一点。

评论


\ $ \ begingroup \ $
因此,如果循环条件包含例如&& count \ $ \ endgroup \ $
–没人远离SE
2013年12月8日在16:19



\ $ \ begingroup \ $
当然可以。
\ $ \ endgroup \ $
–rolfl
13年12月8日在16:27

\ $ \ begingroup \ $
@rolfl,这是在其中将i ++更改为++ i时会更改数组中分配的内容的实例之一吗?您能再解释一下吗,所以当有人输入[++ count]时不会感到困惑
\ $ \ endgroup \ $
–马拉奇♦
2013年12月10日19:02



\ $ \ begingroup \ $
@Malachi它将更改其在数组中的分配位置。对于x = 0; y = 0,您将通过count ++分配给位置0,并通过++ count分配给位置1。 (假设循环之前count =0。)
\ $ \ endgroup \ $
– ANeves认为SE是邪恶的
2013年12月10日19:42

#3 楼

我认为@rolfl在正确的轨道上

for (int x = 0; x < max_x_index; x++) {
    for (int y = 0; y < max_y_index; y++) {
        some2Darray[count++] = ...;
    }
}


使用for循环而不是

/* initialization */;
while (/* condition */) {
    /* body */;

    /* increment */;
}
/>
是为了使您的代码具有与目的相符的立即可识别的外观。因此,初始化,条件和增量部分都应达到相同的目的。您应该避免在for循环标头中引入“脱离主题”的代码。此外,some2Darray[count++] = ...仅在一行中有效地传达了您要填充的数组-编写一个元素,然后移动


此处的其他用户表示反对,因为循环体中存在某些意外的流量控制,可能会绕过count++。该代码的目标似乎是生成一个平面2D数组,因此我怀疑是否涉及到breakcontinue。但是,如果仍然担心一致性,则可以使用断言。

int count = 0;
for (int x = 0; x < max_x_index; x++) {
    for (int y = 0; y < max_y_index; y++) {
        some2Darray[count++] = ...;
    }
}
Debug.Assert(count == max_x_index * max_y_index);


或者,完全消除count,而仅使用x * max_y_index + y

评论


\ $ \ begingroup \ $
正如那些指出流量控制可能错过尾随增量的人之一,我同意在给定的用法中这不会发生。但是,在对代码评审中接受的内容提供一般指导时,必须考虑这一点。
\ $ \ endgroup \ $
– Michael Urman
2013年12月8日22:24

\ $ \ begingroup \ $
“ Count”比“ x * max_x_index + y”更清晰,并且如果数组变得更加复杂(例如,如果某些迭代将零个元素或多个元素推入多个元素),则它可以继续工作
\ $ \ endgroup \ $
–马特
2013年12月9日,下午1:54

#4 楼

这种更改存在正确性问题。在给定的示例中,变换后的循环是等效的(除了增量发生的顺序;我假设它们都是int变量,因此不太可能发生问题。)

循环中可以包含continue语句,这两者之间突然之间有很大的不同:

int count = 0;
for (int x = 0; x < MAX; ++x, ++count)
{
    : : :
    if (sometest())
        continue;
    : : :
}




int count = 0;
for (int x = 0; x < MAX; ++x)
{
    : : :
    if (sometest())
        continue;
    : : :
    ++count;
}


在后面的代码块中,仅当循环主体完成时,count才增加。在前一个代码块中,每当开始下一个循环迭代时,count就会递增。用例可以确定其中哪个是正确的,并且在这些情况之间的决定可能值得评论,以免稍后再破坏它。

请注意,将++count移至第二个示例的开头将使其有所不同。与第一个示例更相似,但在您的示例中将需要使用count - 1索引数组。作为类似的选择,如果只使用一次索引,则可以使用诸如some2Darray[count++]之类的结构,而不要使用其中的任何一种,尽管像原始的第二个代码块一样,这也可能会因早期continue的行为而产生错误,并且有些人会认为它的可读性较低。 br />

#5 楼

此线程上的几个人都在讨论for循环的工作原理,但很少有人提到for循环和while循环之间的语义差异。

当程序员在阅读代码时遇到for循环时,语义是循环肯定会终止并在一定范围的数字或元素列表上进行迭代。这就是为什么所有程序员都立即知道以下循环的原因,并且在脑海中很容易理解和解析它们。

// iterate over the integer range N...M
for(int i = N; i < M; i++)
   func(N)

// iterate over every element in array
for(int i = 0; i < array.length; i++)
   func(array[i]);

// iterate over some singly linked list
for(item = list; item != NULL item=item->Next)
   func(item);

// iterate over some doubly linked list:
for(item = listHead->Next; item != listHead; item = item->Next)
   func(item);


因此,您的前两个数组精心设计,并且在代码审查期间快速且易于理解。这两个列表在[0,0]到[max_x_index,max_y_index]的所有项上迭代x和y。但是如果您毒害了两个for循环之一,则它们看起来不再正常通过将一些与语义无关的变量(计数)添加到其中一个循环中,突然整个循环变得难以理解。计数与循环中的[x,y]迭代无关。它在语义上与some2DArray中有多少个元素相关,并且将它们彼此移开会使整个代码更难阅读,不可避免地也难以维护和调试。

实践是修改变量以使其含义更接近语义,这有助于理解代码。为此,您应该在应更改计数的位置修改count-不是在循环迭代时,而是在通过内联增量修改some2DArray时:

 some2DArray[count++] = ...


或紧随其后:

 some2DArray[count] = ...
 count++


因此,回答您的问题,在for循环中增加多个变量几乎从来不是一个好主意。 for循环应专门处理要迭代的修改元素,而不要理会其他变量。违反此规则会混淆程序的语义,并使其难以阅读。

#6 楼

您在评论中提到,最终结果最终将是将数组的每个成员设置为x * y y重置x的每个迭代。 count将以值x * y结尾。 – afuzzyllama 1小时前


所以这是我的想法,而不是这样做:

for(int x = 0; x < max_x_index; x++){
    for(int y = 0; y < max_y_index; y++, count++){ 
    {
        some2DarrayRepresentedBy1D[count] = ...;
    }
} 


执行此操作

for (int i = 0; i < max_x_index * max_y_index; i++)
{
    some2DarrayRepresentedBy1D[i] = ...;
}


很明显,我不知道您在...中做什么,但是如果您嵌套在两个For循环中,并且这里没有太多其他事情,那么我认为您是每次都在做同样的事情。这看起来像您可能正在寻找的答案,否则,您需要显示更多代码。

评论


\ $ \ begingroup \ $
大概原始代码中的省略号是someProcess(someMatrix [x] [y])。有了这个建议,它必须是someProcess(someMatrix [i / max_y_index] [i%max_y_index])。我不会在清晰度或性能上有所改善。
\ $ \ endgroup \ $
– 200_success
2013年12月12日23:16

\ $ \ begingroup \ $
@ 200_success,这个公式对我来说似乎不合适,但是如果是这种情况,那么像我的其他答案一样执行它就很有意义。但即使OP完全在问其他问题,该信息也应该是问题
\ $ \ endgroup \ $
–马拉奇♦
2013年12月12日23:21

\ $ \ begingroup \ $
@Malachi-问题的关键是要询问count在for声明中的位置以及它是否常见。
\ $ \ endgroup \ $
–afuzzyllama
2013年12月13日,0:56



\ $ \ begingroup \ $
@afuzzyllama我知道,我一句话一句话地读了这个问题,意识到我离你的要求很远。但我希望它也为您提供了另一种查看代码的方式。从200_success的评论来看,尽管我认为这个特定的答案不起作用。我的其他答案应该对您来说很好,并且似乎没有违反任何规则
\ $ \ endgroup \ $
–马拉奇♦
2013年12月13日在3:27

#7 楼

UPDATE

Matt提出了非常好的要点,但我仍然相信下面的两个代码块将与Matt在其回答中所说的一样。

count每次迭代都会增加for循环的内部,因此它正在两个代码块中更改应更改的位置。

不应递增未在For循环的声明中声明的变量。

那样说,您可以这样操作

for(int x = 0, int count = 0; x < max_x_index; x++){
    for(int y = 0; y < max_y_index; y++, count++){ 
    {
        some2DarrayRepresentedBy1D[count] = ...;
    }
}


如果您不编写代码,这将非常令人困惑,从而违反了KISS原则和/或POLS / POLA原则(最不惊奇/惊讶的原则)

离开Rolfl给出的答案,这将减少编写代码的时间,并且可能会更简洁一些,请再次评论此代码包含您的工作方式以及执行此操作的原因。
for(int x = 0, int count = 0; x < max_x_index; x++){
    for(int y = 0; y < max_y_index; y++){ 
    {
        some2DarrayRepresentedBy1D[count++] = ...;
    }
}




一点点的知识

>您可以做什么

for(int x = 0; x < max_x_index; x++){
    for(int y = 0; y < max_y_index; y++){ 
    {
        some2DarrayRepresentedBy1D[count] = ...;
        count++;
    }
}


因为增量器仅用于进行循环(取决于您的计数,[此示例将为您提供完全不同的结果])

for (int x = 0, int y = 100;x < max_x_index, y > max_y_index; x++, y--){
    some2DarrayRepresentedBy1D[count] = ... ;
    count++;
}


我只举一个例子,您可以在一个for循环中使用两个增量器,但是OP发布的不是这些实例之一。

评论


\ $ \ begingroup \ $
我认为不是KISS,而是最不惊奇的原则?
\ $ \ endgroup \ $
–没人远离SE
2013年12月10日18:57

\ $ \ begingroup \ $
@没人就是我要评论的人:“你是说POLS” :)
\ $ \ endgroup \ $
– Mathieu Guindon♦
2013年12月10日19:06

\ $ \ begingroup \ $
如果要将变量放入for循环中并消除多余的行,也许不是多余的注释或字符,这是增加该变量的最佳方法。我很乐意看到性能统计数据将两种方式相互对照
\ $ \ endgroup \ $
–马拉奇♦
2013年12月10日19:26