在C++ for循环之前从未见过

Tho*_*mas 163 c# c++ for-loop

我正在将C++算法转换为C#.我遇到了这个for循环:

for (u = b.size(), v = b.back(); u--; v = p[v]) 
b[u] = v;
Run Code Online (Sandbox Code Playgroud)

它在C++中没有错误,但在C#中没有错误(不能将int转换为bool).我真的无法弄清楚这个循环,条件在哪里?

有人可以解释一下吗?

PS.只是为了检查,使VECTOR适应LIST b.back()对应b [b.Count-1]?

das*_*ght 319

for循环的条件位于中间 - 两个分号之间;.

在C++中,几乎可以将任何表达式作为条件:任何评估为零的意思false; 非零意味着true.

在您的情况下,条件是u--:当您转换为C#时,只需添加!= 0:

for (u = b.size(), v = b.back(); u-- != 0; v = p[v]) 
    b[u] = v; //                     ^^^^ HERE
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一句,托马斯也可能因使用逗号而感到困惑,它与分号非常不同,它允许你在for循环的一个部分中做多个事情(在这种情况下,它初始化了两个变量).上次我检查这些不寻常的结构时不被认为是最可读的解决方案,因此可能会被一些人所厌恶. (55认同)
  • @Roger这不会是相同的循环,因为你在循环结束时递减u而不是开始(即在b [u] = v而不是之前).实际上你需要用'u = b.size() - 1`来初始化它. (7认同)
  • 如果我没记错的话,包含逗号的表达式具有右边最后一个子表达式的值.它来自C,可以在任何表达式中使用,而不仅仅是在for循环中. (2认同)

sli*_*lim 165

很多准确的答案,但我认为值得写出等效的while循环.

for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;
Run Code Online (Sandbox Code Playgroud)

相当于:

u = b.size();
v = b.back();
while(u--) {
   b[u] = v;
   v = p[v];
}
Run Code Online (Sandbox Code Playgroud)

在转换为C#时,您可以考虑重构为while()格式.在我看来,它更清晰,更少是新程序员的陷阱,同样有效.

正如其他人已经指出的那样 - 但为了使我的答案完整 - 要使它在C#中工作,你需要while(u--)改为while(u-- != 0).

......或者while(u-- >0)万一你开始消极.(好吧,b.size()永远不会消极 - 但考虑一个普遍的情况,或许其他东西初始化你).

或者,使其更清晰:

u = b.size();
v = b.back();
while(u>0) {
   u--;
   b[u] = v;
   v = p[v];
}
Run Code Online (Sandbox Code Playgroud)

最好是要清楚而不是简洁.

  • 这个答案不仅澄清了不良代码,而且还提供了一个很好的选择.1起!! (29认同)
  • 我不认为你的代码必须更加清晰."for"而不是"while"的要点正是你将初始化和递增/递减放在一个语句中,这并不一定会使代码更难理解.否则,我们根本不应该使用`for`. (9认同)
  • 顺便说一句,我会小心`while(u--> 0)`形式.如果间距搞砸了,你最终会得到一个"从零到零"的循环:`while(u - > 0)`,这往往会让所有人乍一看.(_我不确定它是否是有效的C#,但是它在C中,我认为它在C++中可能也一样?_) (2认同)
  • @Izkata:它不是一个运算符,它被解析为` - `标记后跟一个`>`标记.两个独立的运营商 "下降到零"循环只是后递减和大于后的直接组合.C++运算符重载不会创建新的运算符,它只是重新利用现有运算符. (2认同)

Dan*_*nas 66

条件是u--;,因为它位于for指令的第二个位置.

如果值u--;不为0,则将其解释为true(即隐式转换为布尔值true).相反,如果它的值为0,则它​​将被转换为false.

这是非常糟糕的代码.

更新:我在这篇博文中讨论了"for"循环的写作.其建议可归纳为以下段落:

for循环是一个实用的,可读的(一旦你习惯了)和简洁的构造,但你需要很好地使用它.由于其不常见的语法,以过于富有想象力的方式使用它并不是一个好主意.

for循环的所有部分都应该简短易读.应选择变量名称以使其易于理解.

这个例子显然违反了这些建议.

  • 出于一个非常简单的原因,这是*可怕的*代码; 当你阅读它时,你无法轻易理解它.这是"聪明","黑客"; 它利用编码结构的组合,以及他们如何在幕后工作的知识,创建一个完成工作但却无法理解的结构,因为它不是语言作者所设想的形式,而是传达给大多数人语言用户. (48认同)
  • 我不明白为什么这么多人似乎认为**u - **是非常糟糕的代码只是因为缺少(隐含在C++中)**!= 0**.当然,任何使用代码的人都会完全意识到*0 = false,其他每个值= true*.关于**u**的前/后递增的混淆的范围要大得多,或者人们可能假设**u = b.size()**将*始终*执行**v = b.back之前( )**(我的理解是执行顺序未定义,但我有待纠正). (14认同)
  • 很好的答案.我会+1它...如果不是"这是非常糟糕的代码." 声明;) (4认同)
  • 或者它会停在u == 0可能......? (3认同)
  • 没有; 在C++中有一个从int到bool的隐式转换,0转换为false.当u-- == 0时,循环将终止.在C#中,没有这样的隐式转换,所以你必须明确地说u-- == 0.编辑:这是对你的第一个评论的回应. (2认同)
  • @Thomas:循环不是(必然)无限.每次评估时,_u_的值都会递减.当u为0时,u--将评估为0,因此循环将停止(并且_u_进一步递减,当然,最终值为-1). (2认同)
  • @FumbleFingers:单独的隐式演员(虽然我鄙视它)不是唯一的原因,也不是主要原因.我同意你的看法,前后增加的潜在混淆更为重要.初始化部分中两个语句的组合只会增加它,并且整体结果非常难以理解,超出了我对难以维护的代码的个人容忍度 - 这实际上是非常小的,但是有充分的理由并经过多年的经验开发者. (2认同)

Nar*_*dra 23

这将是循环的C#形式.

// back fetches the last element of vector in c++.
for (u = b.size(), v = b.back(); (u--) != 0; v = p[v]) 
{      
  b[u] = v;      
}
Run Code Online (Sandbox Code Playgroud)

只需替换size()和back()的等效项.

它的作用是反转列表并存储在数组中.但是在C#中我们直接为此设置了系统定义的函数.所以你也不需要编写这个循环.

b = b.Reverse().ToArray();
Run Code Online (Sandbox Code Playgroud)

  • @Rain问题是`v = b.back()`.你让它在每次循环迭代中执行而不仅仅是第一次 - 我们不知道`back()`是什么(有副作用吗?它是否改变了`b`的内部表示?),所以这个循环_not_等于问题中的循环. (5认同)

Bo *_*son 14

条件是结果u--,u它是递减之前的值.

在C和C++中,通过隐式地进行比较,int 可以将a转换为bool != 0(0是false,其他一切都是true).

b.back()是容器中的最后一个元素,即b[b.size() - 1]何时size() != 0.


Joe*_*oey 11

在C中,所有非零都true在"布尔"上下文中,例如循环结束条件或条件语句.在C#中,您必须明确地进行检查:u-- != 0.


Jon*_*nna 6

正如其他人所说,C++隐式转换为布尔值的事实意味着条件是u--,如果值非零,则为真.

值得补充的是,你在问"有条件的地方"时有一个错误的假设.在C++和C#(以及其他类似的语法语言)中,您可以使用空条件.在这种情况下,结果始终为true,因此该循环永续,或者直到一些其他条件,即可退出它(通过return,breakthrow).

for(int i = 0; ; ++i)
  doThisForever(i);
Run Code Online (Sandbox Code Playgroud)

实际上,for语句的任何部分都可以省略,在这种情况下它只是没有执行.

一般来说,for(A; B; C){D}for(A; B; C)D;成为:

{A}
loopBack:
if(!(B))
  goto escapeLoop;
{D}
{C}
goto loopBack;
escapeLoop:
Run Code Online (Sandbox Code Playgroud)

可以省略A,B,C或D中的任何一个或多个.

因此,有些人喜欢for(;;) 无限循环.我这样做是因为虽然while(true)它更受欢迎,但我读到"直到真相结束才真实",与我的阅读for(;;) "永远" 相比,这听起来有点世界末日.

这是一个品味问题,但因为我不是世界上唯一一个喜欢for(;;)它的人,值得知道这意味着什么.