从未见过 C++ for 循环

Posted

技术标签:

【中文标题】从未见过 C++ for 循环【英文标题】:Never seen before C++ for loop 【发布时间】:2012-07-29 03:36:37 【问题描述】:

我正在将 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] ?

【问题讨论】:

条件在哪里?那将是u--。分号用于分隔for 语句的各个部分。 这是一个很正常的循环。 C# 不会将数字隐式转换为布尔值,因此您需要将条件转换为 ; u-- != 0; @Jessie Good——好的代码与语言允许的内容无关,它与不熟悉的同事阅读代码所需的时间有关。如果它引起任何混乱,即使它是合法的,它也不是最好的解决方案。通常,更冗长的解决方案比简洁的解决方案要好得多,并且大多数编译器都会以任何一种方式编译成相同的东西。 我希望,在转换代码之后,你给变量起一个比buv等更好的名字。他们这样命名的唯一原因是因为有人想要通过使他们的代码不可读来看起来很聪明。 @houbysoft:这是一个一般的 *** 问题。如果你在一个特定的研究领域提出一个非常详细、经过充分研究和有趣的问题,这会导致一个棘手而有趣的问题的解决方案,并且在经过几天的艰苦研究后回答这样一个问题,你只会得到几个十几位参观者和该领域的几位专家的一两个赞成票。如果您想快速获得大量代表,您必须提出并回答此类问题。 “我如何在 php 中添加两个数字”、“do 在 C++ 中的含义” - 将获得成千上万寻找教程的初学者的点击。 【参考方案1】:

for 循环的条件在中间 - 在两个分号 ; 之间。

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

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

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

【讨论】:

顺便说一句,Thomas 可能也对逗号的使用感到困惑,它与分号有很大不同,它可以让您在 for 循环的一个部分中执行多项操作(在此在这种情况下,它初始化了两个变量)。上次我检查这些不寻常的结构时,可能并不认为是最易读的解决方案,因此可能会被一些人不赞成。 如果我没记错的话,包含逗号的表达式具有右侧最后一个子表达式的值。这来自 C 语言,可以在任何表达式中使用,而不仅仅是在 for 循环中。 @Roger 这不是同一个循环,因为您在循环结束时递减 u 而不是开始(即在 b[u] = v 之后而不是之前)。事实上,你需要用u = b.size() - 1 来初始化它。 仅供参考:我刚刚让你超过了 500K 的限制。这就像在某个地方成为第 100 万个客户并赢得一些东西一样吗?无论如何:非常祝贺;始终期待您的精确、尖锐的答案!继续;见到你,在本世纪晚些时候制造 100 万件产品。【参考方案2】:

很多准确的答案,但我认为值得写出等效的 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];

简洁比简洁好。

【讨论】:

这个答案不仅澄清了错误的代码,而且还提供了一个很好的选择。 1起!! 顺便说一句,我会小心while (u-- >0) 表单。如果间距搞砸了,你可能会得到一个“归零”循环:while (u --> 0),乍一看这往往会使每个人都感到困惑。 (我不确定它是否是有效的 C#,但它是在 C 中的,我认为它在 C++ 中可能也是如此? 我认为您的代码不一定更清晰。 for 而不是 while 的意义恰恰在于您将初始化和递增/递减放在一个语句中,这并不一定会使代码更难理解。否则,我们根本不应该使用for 尽可能少地重写代码也很重要。 (毕竟 for 循环可能会改变。)你已经把“u--”放在两个不同的地方,而且循环并不是很清晰(我一眼就能看出 for 循环的作用;我必须扫描多行)。简洁也有好处。不要轻视他们。尽管如此,这是一个很好的例子,说明它可以如何编写。对于不习惯 C++ 中的 for 语句(甚至可能根本不习惯)的人来说,它确实更容易理解。 @Izkata:它永远不是运算符,它被解析为 -- 令牌,后跟 > 令牌。两个独立的运算符。 “降到零”循环只是后递减和大于的简单组合。 C++ 运算符重载不会创建新的运算符,它只是重新利用现有的运算符。【参考方案3】:

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

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

这是very bad code。

更新:我在blog post 中讨论了“for”循环的编写。其建议可总结为以下段落:

for 循环是一个实用的、可读的(一旦你习惯了它)并且 简洁的结构,但你需要很好地使用它。因为不常见 语法,以一种过于想象的方式使用它不是一个好主意。

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

这个例子明显违反了这些建议。

【讨论】:

或者它可能会停在 u == 0...? 否;在 C++ 中有一个从 int 到 bool 的隐式转换,其中 0 转换为 false。当 u-- == 0 时,循环将终止。在 C# 中,没有这种隐式转换,因此您必须明确地说 u-- == 0。编辑:这是对您的第一条评论的回应。 这是糟糕的代码,原因很简单;当你阅读它时,你不能轻易理解它。这是“聪明”,是“黑客”;它利用编码结构的组合,以及它们如何在幕后工作的知识,来创建一个可以完成工作但难以理解的结构,因为它不是语言作者所设想的形式,也没有传达给大多数人语言用户。 优秀的答案。我会 +1 它......如果不是“这是非常糟糕的代码”。声明 ;) 我不明白为什么这么多人似乎认为 u-- 是非常糟糕的代码,仅仅是因为缺少(在 C++ 中隐含)!= 0 。当然,任何使用代码的人都会非常清楚0=false,其他所有值=true。关于 u 的前/后递增,或者人们可能认为 u = b.size()总是v = b.back() 之前执行(我的理解是那里的执行顺序未定义,但我有待更正)。【参考方案4】:

这将是循环的 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();

【讨论】:

通过将v = b.back();从for初始化器中取出,您不只是改变了它的工作方式,因为v = p[v]被覆盖了 v = p[v] 不会对最终结果产生任何影响,因为该行将在最后执行。之后 v 不在循环中引用。这一行只是为了展示如何将循环从 c++ 转换为 C#。 在 c++ 代码中,v = b.back(); 在迭代开始前执行一次,v = p[v] 在每次迭代开始时执行。在那个 C# 版本中,v = p[v] 仍然在每次迭代开始时执行,但v = b.back(); 在它之后立即执行,为下一条指令 b[u] = v; 更改 v 的值。 (也许问题在您阅读后被编辑) @Rain 问题是v = b.back()。你让它在每次循环迭代时执行,而不仅仅是第一个循环迭代 - 我们不知道 back() 做了什么(有任何副作用吗?它会改变 b 的内部表示吗?),所以这个循环是等同于问题中的那个。 @Rain 没错,你自己仔细看看。 初始化步骤只在循环开始前发生一次,不是每次迭代开始时发生。如果将 v = b.back() 移到循环之外,您的代码将是正确的。 (另外,如果您想回复某人,请在其姓名前使用@,以便我们收到通知)【参考方案5】:
u = b.size(), v = b.back()

正在初始化。

u--

是条件。

v = p[v]

是迭代

【讨论】:

【参考方案6】:

条件是u-- 的结果,也就是u 递减前的值。

在 C 和 C++ 中,int 可通过隐式进行 != 0 比较转换为 bool(0 为 false,其他所有内容为 true)。

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

【讨论】:

【参考方案7】:

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

【讨论】:

这不是OP的问题。他正在询问对终止条件的评估(即本例中的“u--”)。【参考方案8】:

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

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

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

确实,for 语句的任何部分都可以省略,在这种情况下,它只是不执行。

一般来说,for(A; B; C)Dfor(A; B; C)D; 变为:

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

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

因此,有些人喜欢 for(;;) 进行无限循环。我这样做是因为虽然while(true) 更受欢迎,但我将其读作“直到真相结束”,与我将for(;;) 读为“永远”相比,这听起来有点世界末日。

这是一个品味问题,但由于我不是世界上唯一喜欢 for(;;) 的人,因此值得了解它的含义。

【讨论】:

【参考方案9】:

所有答案都是正确的:-

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.

【讨论】:

【参考方案10】:
for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

在上面的代码中,uv 被初始化为 b.size()b.back()

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

for 循环将在 u 变为 0 时退出。

【讨论】:

【参考方案11】:

C#中遇到的错误本身就解决了这个疑问。 for 循环搜索

错误

条件终止。众所周知,

(BOOL) FALSE = (int) 0

但与 C++ 不同,C# 无法自行处理此问题。所以你要搜索的条件是

你--

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

u-- != 0

u--> 0

但还是尽量避免这种编码习惯。

while 循环

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

for循环。

【讨论】:

@Downvoter:如果您对解决方案不满意,投反对票是可以的,但同时请花时间说明原因,以便改进答案。【参考方案12】:

如果您习惯于 C/C++,那么这段代码并不难阅读,尽管它非常简洁,代码也不是那么好。所以让我解释一下比其他任何东西都更像 Cism 的部分。首先,C for 循环的一般语法如下所示:

for (<initialization> ; <condition>; <increment>)

    <code...>

初始化代码运行一次。然后在每个循环之前测试条件,最后在每个循环之后调用增量。所以在你的例子中你会发现条件是u--

为什么u-- 在 C 而不是 C# 中作为条件工作?因为 C 隐式地将很多东西转换为 bools 并且可能会导致麻烦。对于一个数字,任何非零都是真,零是假。所以它会从 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() 是无符号的,它会迭代很长时间。 (另外,你错过了一个分号。)但至少你没有像许多其他答案和 cmets 那样采用“'Dick and Jane'是好英语”的方法,称赞荒谬的冗长重写只会更容易供新手和其他不熟练的程序员阅读。

以上是关于从未见过 C++ for 循环的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript `of` 关键字(for...of 循环)

在“for”循环中以 1 递增时,是不是有使用 > (<) 而不是 != 的技术原因?

哪个更好,单 for 循环或双 for 循环迭代二维数组? C++

C++基于范围的for循环详解

c++中for循环和switch语句哪个更高效

c++两个嵌套的for循环使用continue?