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


164

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

for (u = b.size(), v = b.back(); u--; v = p[v]) 
b[u] = v;

它在C ++中没有错误,但在C#中却没有(无法将int转换为bool)。我真的不知道这个for循环,条件在哪里?

有人可以解释一下吗?

PS。只是检查一下,以使VECTOR适应LIST,b.back()是否对应于b [b.Count-1]?


35
条件在哪里?那会是u--。分号用于分隔for语句的各个部分。
David Heffernan

77
这是一个很正常的循环。C#不会将数字隐式转换为布尔值,因此您需要将条件转换为; u-- != 0;
R. Martinho Fernandes

28
@Jessie Good-好的代码与该语言所允许的内容无关,它与一个无经验的同事阅读该代码要花多长时间有关。如果这引起任何混乱,即使是合法的,也不是最好的解决方案。通常,更冗长的解决方案要比简洁的解决方案好得多,并且大多数编译器都会以相同的方式对相同的东西进行编译。
Bill K

18
我希望,转换后的代码,你给变量优于名字buv等他们被这样命名的唯一原因是因为有人想通过使他们的代码不可读智能查找。
2012年

33
@houbysoft:这是一个一般的stackoverflow问题。如果您在特定的研究领域中提出了一个非常详细,经过深入研究且有趣的问题,从而为一个棘手且有趣的问题提供了解决方案,而在经过艰苦的一天的研究之后回答了这个问题,您将只会得到一些问题。来自该领域的几位专家的十几位访客和一两个投票。如果您想快速获得很多代表,则必须提出并回答类似这样的问题。“如何在php中添加两个数字”,“ do在C ++ 中意味着什么”-会从成千上万的初学者中寻找教程。
vsz 2012年

Answers:


320

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

在C ++中,可以将几乎所有表达式都作为条件:任何计算结果为零的均值false;非零均值true

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

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

55
顺便说一下,Thomas可能也因使用逗号而感到困惑,它与分号非常不同,它可以让您在for循环的一个部分中执行多项操作(在这种情况下,它初始化了两个变量)。上次我检查这些不寻常的构造并不是最易读的解决方案,因此可能被某些人皱眉。
Bill K

2
如果我没记错的话,包含逗号的表达式的值将在右侧显示最后一个子表达式的值。它来自C,可以在任何表达式中使用,不仅可以在for循环中使用。
乔治

7
@Roger不会是同一循环,因为您要在循环的末尾而不是开始递减u(即在b [u] = v之后而不是之前)。实际上,您将需要使用进行初始化u = b.size() - 1
Didier L

Fyi:我刚刚将您的费用超过了50万。就像在某个地方成为百万分之一的客户并赢得一分收获吗?无论如何:许多祝贺;始终期待着您的精确,敏锐的答案!继续; 见到你在本世纪晚些时候制造了100万件东西。
GhostCat

165

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

for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

等效于:

u = b.size();
v = b.back();
while(u--) {
   b[u] = v;
   v = p[v];
}

您可能会在转换为C#时考虑重构为while()格式。在我看来,它更清晰,对于新程序员来说更是无处可寻,并且同样有效。

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

...或者while(u-- >0)以防万一你开始从负面开始。(好的,b.size()永远不会否定的-但考虑一个一般情况,其中可能有其他初始化u)。

或者,使其更加清晰:

u = b.size();
v = b.back();
while(u>0) {
   u--;
   b[u] = v;
   v = p[v];
}

清楚胜于简洁。


29
这个答案不仅可以弄清错误的代码,而且可以提供很好的选择。1!
polvoazul 2012年

2
顺便说一句,我会小心while (u-- >0)表格。如果间距变得混乱,您可能会遇到“降至零”循环:while (u --> 0),这乍一看往往会使所有人感到 困惑。(我不确定它是否有效的C#,但它在C中,并且我认为在C ++中也可能一样?
Izkata 2012年

9
我认为您的代码不一定更清晰。for代替的要点while就是,将初始化和递增/递减放在一条语句中,并不一定会使代码更难理解。否则,我们根本不应该使用for
musiphil

1
重写尽可能少的代码也很重要。(毕竟,for循环可能会发生变化。)您已经将“ u--”放在两个不同的位置,并且该循环还不是很清晰(我可以一眼看出for循环的作用;我必须扫描一下多行)。简洁也有好处。不要轻视他们。尽管如此,这还是一个很好的例子,说明了如何编写它。对于确实不习惯使用C ++语句的任何人(甚至可能根本不熟悉),它的确使您更容易理解。
NotKyon 2012年

2
@Izkata:它从来都不是运算符,它被解析为--令牌,然后是>令牌。两个独立的运算符。“降到零”循环只是后减量和大于量的直接组合。C ++运算符重载不会创建新的运算符,而只是重新利用现有的运算符。
Ben Voigt 2012年

66

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

如果的值u--;不同于0,则将其解释为true(即,隐式转换为boolean值true)。相反,如果其值为0,则将其强制转换为false

这是非常糟糕的代码

更新:我在此博客文章中讨论了“ for”循环的编写。其建议可以归纳为以下几段:

for循环是一种实用,可读(一旦您习惯了)的简洁结构,但是您需要很好地使用它。由于其语法不常见,以太富于想象力的方式使用它不是一个好主意。

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

此示例明显违反了这些建议。


3
否则它将在u == 0处停止...?
托马斯(Thomas)

2
没有; 在C ++中,存在从int到bool的隐式转换,其中0转换为false。当u-- == 0时,循环将终止。在C#中,没有这种隐式转换,因此您必须明确地说出u-- ==0。编辑:这是对您的第一条评论的回应。
克里斯

48
这是一个糟糕的代码,原因很简单。您在阅读时不容易理解。是“聪明”,是“骇客”;它利用编码结构以及它们在幕后的工作方式的知识的组合,来创建一个可以完成工作但又缺乏理解的结构,因为它不是语言作者所设想的形式,并且没有传达给大多数人语言用户。
KeithS 2012年

4
极好的答案。我会为其+1,如果不是的话“这是非常糟糕的代码”。声明;)
Sandman4 2012年

14
我不明白为什么这么多人似乎认为u--仅仅是因为丢失(在C ++中是隐含的)!= 0真的是不好的代码。当然,使用代码的任何人都将完全意识到0 = false,其他每个值= true。有混乱关于前/后递增,远远范围ü,或人们也许假设U = b.size()始终之前执行V = b.back() (我的理解是执行顺序有不确定的,但我会得到纠正)。
FumbleFingers

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

只需替换size()和back()的等效项即可。

它的作用是反转列表并将其存储在数组中。但是在C#中,我们直接为此具有系统定义的功能。因此,您也不需要编写此循环。

b = b.Reverse().ToArray();

1
通过采取v = b.back();将用于初始化器没有你只需要改变工作方式的出来,因为v = p[v]被覆盖
若昂·波特拉

v = p [v]对最终结果没有任何影响,因为该行将在最后执行。之后,在循环中不引用v。该行仅用于显示循环如何从c ++转换为C#。
纳伦德拉

1
C ++代码中的C ++代码v = b.back();在迭代开始之前执行一次,并v = p[v]在每次迭代开始时执行。在该C#版本v = p[v]中,仍然在每次迭代的开始执行,但v = b.back();在之后执行,更改v了下一条指令的值b[u] = v;。(也许这个问题是编辑阅读后)
若奥·波特拉

5
@Rain问题是v = b.back()。您让它在每个循环迭代中执行,而不仅仅是在第一次循环中执行-我们不知道这是什么back()(有副作用吗?它会更改?的内部表示b),因此此循环等于问题。
伊兹卡塔

1
@Rain确实,您自己仔细看一下。在初始化步骤只发生一次的循环开始前,没有在一开始每次迭代。如果将代码v = b.back()移到循环的上方,则该代码是正确的。(此外,如果您要回复某人,请@在其姓名前使用,这样我们会收到通知)
Izkata 2012年


14

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

在C和C ++编写的int 转换为bool通过隐含做!= 0比较(0 false,其他一切都是true)。

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


11

在C语言中,所有非零值都true位于“布尔”上下文中,例如循环结束条件或条件语句。在C#中,你必须作出这样的检查明确:u-- != 0


这不是OP的问题。他正在询问终止条件的评估(在这种情况下,即“ u--”)。
ApplePie 2012年

6

如其他人所述,C ++具有隐式转换为布尔值的事实意味着条件为u--,如果值非零,则为true。

值得补充的是,在询问“条件在哪里”时,您有一个错误的假设。在C ++和C#(以及其他类似语法的语言)中,您都可以有一个空条件。在这种情况下,结果始终为true,因此该循环永续,或者直到一些其他条件,即可退出它(通过returnbreakthrow)。

for(int i = 0; ; ++i)
  doThisForever(i);

确实,可以忽略for语句的任何部分,在这种情况下就不会执行。

一般而言,for(A; B; C){D}for(A; B; C)D;变为:

{A}
loopBack:
if(!(B))
  goto escapeLoop;
{D}
{C}
goto loopBack;
escapeLoop:

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

结果,一些人喜欢for(;;) 无限循环。我这样做是因为虽然while(true)它更受欢迎,但我读到它是“直到真相终成真”,与我读到for(;;) “永远” 相比,这听起来有些启示。

这是一个品味问题,但是由于我不是世界上唯一喜欢for(;;)它的人,所以有必要知道它的含义。


4

所有答案都是正确的:

for循环可以按以下多种方式使用:

Single Statement inside For Loop
Multiple Statements inside For Loop
No Statement inside For Loop
Semicolon at the end of For Loop
Multiple Initialization Statement inside For
Missing Initialization in For Loop
Missing Increment/Decrement Statement
Infinite For Loop
Condition with no Conditional Operator.

4
for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

在上面的代码中,uvb.size()和初始化b.back()

每次条件检查时,它也执行减量语句,即u--

for循环将退出时u会变成0


3

C#本身遇到的错误消除了疑问。for循环搜索

条件终止。而且我们知道

(布尔)假=(整数)0

但是C#不能像C ++那样独自处理它。所以您要寻找的条件是

你-

但是您必须在C#中明确给出条件

u--!= 0

要么

u--> 0

但是仍然要尽量避免这种编码实践。的

while循环

上面的答案是您的最简化的版本之一

for循环。


@Downvoter:只要您对解决方案不满意,就可以进行Downvoting,但同时请花点时间说明原因,以便可以改善答案。
Abhineet

3

如果您习惯使用C / C ++,则此代码虽然不是很简单,但是代码并不是那么好,所以阅读起来并不难。因此,让我解释最重要的部分是Cism。首先,C for循环的一般语法如下所示:

for (<initialization> ; <condition>; <increment>)
{
    <code...>
}

初始化代码只运行一次。然后在每个循环之前测试条件,最后在每个循环之后调用增量。因此,在您的示例中,您将发现条件是u--

为什么u--在C中而不在C#中作为条件工作?因为C隐式地转换了很多东西,所以会引起麻烦。对于数字,任何非零值均为true,零值为false。因此它将从b.size()-1倒数到0。在这种情况下产生副作用有点烦人,最好将其放在for循环的增量部分中,尽管会有很多C代码可以做到这一点。如果我正在写它,我会做的像这样:

for (u = b.size() - 1, v = b.back(); u>=0; --u) 
{
    b[u] = v;
    v = p[v]
}

至少对于我来说,原因更清楚。for循环的每个部分都在做它的工作,仅此而已。在原始代码中,条件是修改变量。增量部分正在执行应在代码块等中执行的操作。

逗号运算符也可能使您陷入循环。在C x=1,y=2语言中,就编译器而言,它看起来像一个语句,并且适合于初始化代码。它只评估每个部分并返回最后一个的值。因此,例如:

std::cout << "(1,2)=" << (1,2) << std::endl;

将打印出2。


重写的问题是,如果b.size()是无符号的,它将迭代很长时间。(而且,您缺少分号。)但至少您没有采用“ Dick and Jane”是很好的英语”方法,因此许多其他答案和评论都采用了这种方法,称赞冗长的冗长重写很容易由新手和其他非熟练程序员阅读。
Jim Balter 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.