C for 循环的实现与其他语言不同?

发布于 2024-07-07 23:28:29 字数 429 浏览 13 评论 0原文

我在 Knuth 的《计算机编程艺术》的评论中读到了以下内容:

“‘实用性’意味着未来的 CS 专业学生必须学习 Kernighan 在设计 C 语言时所犯的错误,特别是一个臭名昭著的事实,即 for 循环评估重复 for 条件,它重复 while 并且无法匹配大多数其他实现 for 循环的语言的行为。”

(http://www.amazon.com/review/R9OVJAJQCP78N/ref=cm_cr_pr_viewpnt #R9OVJAJQCP78N

这家伙在说什么? 如何实现一个不只是 while 循环的语法糖的 for 循环?

I read the following in a review of Knuth's "The Art of Computer Programming":

"The very 'practicality' means that the would-be CS major has to learn Kernighan's mistakes in designing C, notably the infamous fact that a for loop evaluates the for condition repeatedly, which duplicates while and fails to match the behavior of most other languages which implement a for loop."

(http://www.amazon.com/review/R9OVJAJQCP78N/ref=cm_cr_pr_viewpnt#R9OVJAJQCP78N)

What is this guy talking about? How could you implement a for loop that wasn't just syntactic sugar for a while loop?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

玉环 2024-07-14 23:28:29

考虑一下:

for i:=0 to 100 do { ... }

在这种情况下,我们可以通过函数调用替换最终值 100:

for i:=0 to final_value() do { ... }

... 并且 final_value 函数将仅被调用一次。

然而,在 C 中:

for (int i=0; i<final_value(); ++i) // ...

...final_value 函数将在循环中的每次迭代中被调用,因此使其成为更详细的一个好习惯:

int end = final_value();
for (int i=0; i<end; ++i) // ...

Consider this:

for i:=0 to 100 do { ... }

In this case, we could replace the final value, 100, by a function call:

for i:=0 to final_value() do { ... }

... and the final_value-function would be called only once.

In C, however:

for (int i=0; i<final_value(); ++i) // ...

... the final_value-function would be called for each iteration through the loop, thus making it a good practice to be more verbose:

int end = final_value();
for (int i=0; i<end; ++i) // ...
此岸叶落 2024-07-14 23:28:29

如果你想要的只是一个简单的计数循环,那么

for (i=0; i<100; i++) dostuff();

就可以了,编译器可以优化它。

如果您在 for 语句的 continue 部分使用函数,那么

for (i=0; i<strlen(s); i++) dostuff();

每次都会评估该函数,这通常不是一个好主意,因为函数开销会减慢您的进程。 有时它会使您的进程减慢至无法使用的程度。

如果函数的返回值在迭代期间不会改变,请将其从循环中提取:

slen = strlen(s);
for (i=0; i<slen; i++) dostuff();

但是有时函数每次调用都会返回不同的值,然后您不希望将其从循环中提取:

for (isread(fd, &buffer, ISFIRST);
     isstat(fd) >= 0;
     isread(fd, &buffer, ISNEXT)
{
  dostuff(buffer);
}

并且您希望对其进行求值每一次。 (这是一个基于我所做工作的稍微人为的示例,但它显示了潜力)。

C 为您提供了以任何方式滚动循环的原始能力。 您必须知道您的循环应该如何工作,并根据您的需要尽可能地优化它。

最后一个示例可以表示为 while 循环:

isread(fd, &buffer, ISFIRST);
while (isstat(fd) >= 0)
{
  dostuff(buffer);
  isread(fd, &buffer, ISNEXT);
}

但它并不那么简洁,如果我在循环中使用 continue,那么我必须再次调用迭代 isread。 将整个事情放在 for 循环中使其更整洁,并确保迭代 isread 被称为每个循环。

我编写了较低级别的函数,以便它们可以在这样的 for 循环中使用。 它将 while 循环的所有元素组合在一起,以便您可以更轻松地理解它。

If all you want is a simple counting loop, then

for (i=0; i<100; i++) dostuff();

will be fine, and the compiler can optimize it.

If you use a function in the continue part of the for statement, like

for (i=0; i<strlen(s); i++) dostuff();

then the function will be evaluated every time and this is usually not a good idea as the function overheads will slow your process. Sometimes it can slow your process to the point of unusability.

If the function's return value will not change during the iteration, extract it from the loop:

slen = strlen(s);
for (i=0; i<slen; i++) dostuff();

But there are times when the function will be returning different values each call, and then you do not want it extracted from the loop:

for (isread(fd, &buffer, ISFIRST);
     isstat(fd) >= 0;
     isread(fd, &buffer, ISNEXT)
{
  dostuff(buffer);
}

and you want it evaluated each time. (That is a slightly contrived example based on work that I do, but it shows the potential).

C gives you the raw ability to roll your loop any way you can. You have to know how your loop is supposed to work, and you optimize it as best you can, depending on your needs.

That last example could have been expressed as a while loop:

isread(fd, &buffer, ISFIRST);
while (isstat(fd) >= 0)
{
  dostuff(buffer);
  isread(fd, &buffer, ISNEXT);
}

but it's not as neat, and if I use a continue in the loop, then I have to call the iterating isread again. Putting the whole thing in a for loop makes it neater, and ensures that the iterating isread is called each loop.

I write lower-level functions so they can be used in for loops like this. It brings all elements of the while loop together so you can understand it more easily.

鲸落 2024-07-14 23:28:29

他可能指的是像 for i:=0 to N 这样的 for 循环和迭代集合元素的 for-each 循环。 我怀疑所有具有 C 风格 for 循环的语言实际上都是从 C 语言中获得的。

He probably refers to for loops like for i:=0 to N and for-each loops that iterate over the elements of a set. I suspect all languages that have a C-style for loop actually got it from C.

猛虎独行 2024-07-14 23:28:29

Magnus 说得对,但还应该注意的是,在大多数语言(C 之前)中,条件是结束 标准(即“当 i 等于 100 时停止”)。 在 C(以及大多数后 C 语言)中,这是继续标准(即“当 i 小于 100 时继续”)。

Magnus has it right, but one should also note that in most languages (pre-C), the conditional is the end criterion (i.e. "stop when i equals 100"). In C (and most post-C languages), it's the continue criterion (i.e., "continue while i is less than 100").

-黛色若梦 2024-07-14 23:28:29

基本上,C(以及 Java、JavaScript 和许多 C 派生语言)for 循环确实是 while 循环的语法糖:

for (int i = 0; i < max; i++) { DoStuff(); }

这是一个非常流行的习惯用法严格相当于:(

int i = 0; while (i < max) { DoStuff(); i++; }

抛开范围问题,无论如何,范围问题在 C 版本之间都会发生变化。)

每次迭代都会评估停止条件。 在某些情况下这可能很有趣,但也可能是一个陷阱:我看到 i < 源代码中的 strlen(longConstantString) 是减慢程序速度的主要方式,因为 strlen 是 C 语言中的昂贵函数。
不知何故,for循环大多被设计为预先知道运行次数,您仍然可以使用break提前终止,因此停止项的动态评估为烦人多于有用:如果您确实需要动态评估,请使用 while () {} (或 do {} while ())。

在其他一些语言(如 Lua)上,停止条件仅在循环初始化时计算一次。 它有助于预测循环行为,并且通常性能更高。

Basically, the C (and Java, JavaScript, and lot of C-derived languages) for loop is indeed syntactic sugar for a while loop:

for (int i = 0; i < max; i++) { DoStuff(); }

which is a very current idiom is strictly equivalent to:

int i = 0; while (i < max) { DoStuff(); i++; }

(Putting apart scope issues, which changed between versions of C anyway.)

The stop condition is evaluated on each iteration. It can be interesting in some cases, but it can be a pitfall: I saw i < strlen(longConstantString) in a source, which is a major way to slow down a program, because strlen is a costly function in C.
Somehow, the for loop is mostly designed to run a number of times know in advance, you can still use break to terminate early, so the dynamic evaluation of the stop term is more annoying than useful: if you really need dynamic evaluation, you use while () {} (or do {} while ()).

On some other languages, like Lua, the stop condition is evaluated only once, at loop init. It helps predicting loop behavior and it is often more performant.

-黛色若梦 2024-07-14 23:28:29

在 x86 上,for 循环可以在汇编级别完成,而无需进行 while 循环。 loop指令减少寄存器ecx的值,如果ecx大于0则跳转到操作数,否则不执行任何操作。 这是一个经典的 for 循环。

mov ecx, 0x00000010h
loop_start:
;loop body
loop loop_start
;end of loop

On x86, for looping can be done on assembly level without making a while loop. The loop instruction decreases the value of the register ecx and jumps to the operand if ecx is greater than 0, it does nothing otherwise. This is a classic for loop.

mov ecx, 0x00000010h
loop_start:
;loop body
loop loop_start
;end of loop
梦幻的味道 2024-07-14 23:28:29

也许循环展开? 如果您知道 for 循环将执行多少次,您可以直接复制并粘贴循环的内容。 大多数 while 循环将基于某些条件,而不是简单地从 0 计数到 N,因此无法使用此优化。

拿这个例子来说,

int x;
for (x = 10; x != 0; --x)
{
    printf ("Hello\n");
}

我知道你通常会这样做 x = 0; x <= 10; ++x,但所有内容都将在程序集中显示。

一些伪汇编:

mov 10, eax
loop:
print "hello"
dec eax
jne loop

在这个例子中,我们不断跳回循环以打印“hello”10次。 然而,我们每次循环时都会使用 jne 指令评估条件。

如果我们展开它,我们可以简单地输入

print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"

“我们不需要任何其他说明”,因此速度更快。 这只是一个简单的例子 - 我会尝试找到一个更好的例子!

Loop unrolling perhaps? If you know how many time the for loop is going to execute you can literally copy and paste the contents of the loop. Most while loops are going to be be based on some condition that isn't a simple counting from 0 to N, so won't be able to use this optimization.

Take this example

int x;
for (x = 10; x != 0; --x)
{
    printf ("Hello\n");
}

I know you would normally do x = 0; x <= 10; ++x, but all will be revealed in the assembly.

some pseudo assembly:

mov 10, eax
loop:
print "hello"
dec eax
jne loop

In this example we keep jumping back around the loop to print "hello" 10 times. However we are evaluating the condition with the jne instruction each time around the loop.

If we unrolled it we could simply put

print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"
print "hello"

We wouldn't need any of the other instructions, so it is faster. This is only a simple example - I'll try to find a better one!

仲春光 2024-07-14 23:28:29

在 Ada 中(我相信大多数其他 Algol 派生语言),“for”循环的终止条件仅在循环开始时计算一次。 例如,假设您在 Ada 中有以下代码。

q := 10;
for i in 1..q loop
    q := 20;
    --// Do some stuff
end loop;

此循环将恰好迭代 10 次,因为循环开始时 q 为 10。 但是,如果我在 C 中编写看似等效的循环:

q = 10;
for (int i=0;i<q;i++) {
   q = 20;
   // Do some stuff
}

那么循环将迭代 20 次,因为当我变得足够大以使其变得重要时,q 已更改为 20。

当然C方式更灵活。 然而,这有相当多的负面影响。 显而易见的是,程序必须浪费精力在每个周期重新检查循环条件。 一个好的优化器可能足够聪明,可以在像这样的简单情况下解决问题,但是如果“q”是全局的并且“做一些事情”包括过程调用(因此理论上可以修改 q),会发生什么?

严峻的事实是,我们对 Ada 循环的了解远多于对 C 循环的了解。 这意味着,在优化器具有相同水平的智能和努力的情况下,Ada 可以做得更好。 例如,Ada 编译器知道它可以用 10 个内容副本替换整个循环,无论这些内容是什么。 AC 优化器必须检查和分析内容。

这实际上只是 C 语法设计限制编译器的多种方式之一。

In Ada (and I believe most other Algol-derived languages) the terminating condition of a "for" loop is evaluated only once, at the beginning of the loop. For example, suppose you have the following code in Ada

q := 10;
for i in 1..q loop
    q := 20;
    --// Do some stuff
end loop;

This loop will iterate exactly 10 times, because q was 10 when the loop started. However, if I write the seemingly equivalent loop in C:

q = 10;
for (int i=0;i<q;i++) {
   q = 20;
   // Do some stuff
}

Then the loop iterates 20 times, because q was changed to 20 by the time i got large enough for it to matter.

The C way is more flexible, of course. However this has quite a few negative implications. The obvious is that the program has to waste effort rechecking the loop condition every cycle. A good optimizer might be smart enough to work around the problem in simple cases like this, but what happens if "q" is a global and "do some stuff" includes a procedure call (and thus in theory could modify q)?

The hard fact is that we just know way more about the Ada loop than we do about the C loop. That means that with the same level of intelligence and effort in its optimizer, Ada can do a lot better job of optimizing. For instance, the Ada compiler knows that it can replace the entire loop with 10 copies of the contents, no matter what those contents are. A C optimizer would have to examine and analyze the contents.

This is actually just one of many ways where the design of the C syntax hamstrings the compiler.

情泪▽动烟 2024-07-14 23:28:29

这些语言纯粹主义者似乎从未意识到 C 的全部意义,在某种程度上 C++ 的全部意义在于提供了实现您想要的东西以及您想要的方式的可能性。 现在不可否认的是,如果最终表达式的结果发生变化,最好只使用 while 循环。 也许问题是程序员觉得 C 实现了“高级”,但每个人都应该知道 C 是一种相当低级的语言。

What these language purist never seem to realize is the whole point of C, and to an extent C++, is giving the possibility to implement what you want and how you want it. Now admittedly if the result of your end expression changes, it would be better to just use a while loop. Maybe the problem is programmers get the impression C implements a "high level" for, but everyone should know C is a pretty low-level language.

留蓝 2024-07-14 23:28:29

<块引用>

“实用性”意味着未来的 CS 专业学生必须学习 Kernighan 在设计 C 时所犯的错误,特别是一个臭名昭著的事实,即 for 循环重复评估 for 条件,它重复了 while 并且无法匹配大多数情况的行为其他实现 for 循环的语言。

Muaahahahaa!

我喜欢审稿人的基本(双关语)断言:

  • Kernighan 的错误
  • 臭名昭著的事实
  • 无法与大多数其他语言的行为相匹配

对我来说,这闻起来像是一个从未成功掌握 C 的基本功能的人/哲学。

简介

当我还在大学学习物理时,当我发现 C 的 for 循环时,我发现 C(即类 C 语言)将成为我的首选语言。

显然,一个人的风格失败就是另一个人的成功。 本次讨论的主观部分到此结束。

也许 Kernigan 的 for 应该被称为循环?

只是在开玩笑? 也许不是。

该评论的作者显然认为每种语言结构在不同语言之间都应该具有相同的行为。 因此,将 for 重命名为 loop 会减轻他/她的不适。

问题在于作者没有理解C的forwhile的扩展,而不是不同的循环。 这意味着 whilefor 的语法糖精简版本,而不是 while 其他语言中 for 可能具有的被阉割了。

C for 真的需要吗?

引用该评论的作者,他/她对 forwhile 做出以下断言:

  • for 是用于不断循环,从开始到end
  • while 是 for 循环,在每次迭代时评估条件。

当您可以组合正交特征时,它们是一件好事。 但在我所知道的所有语言中,没有办法将 forwhile 组合在一起。

示例:如果对于审阅者的语言,您需要一个从开始到结束的循环(如 for),但仍然能够在每次循环迭代时进行评估(如 while ):您可能需要在容器的子集中(而不是整个容器)找到根据某些状态测试的值?

在类似 C 的语言中,您可以使用 for。 在审阅者的理想语言中,你只是,好吧,破解一些丑陋的代码,因为你没有 for_while 循环(或 C 的 for 循环)而哭泣。

结论

我相信我们可以用以下陈述来总结审稿人对 C(和类 C 语言)的批评:“Kernigan 的臭名昭著的错误,没有为 C 提供以前现有语言的语法”,这可以是再次总结为“永远不要有不同的想法”。

引用 Bjarne Stroustrup 的话:

<块引用>

世界上只有两种语言:人们抱怨的语言和没人使用的语言。

所以,总而言之,审稿人的评论应该算是好评。

^_^

The very 'practicality' means that the would-be CS major has to learn Kernighan's mistakes in designing C, notably the infamous fact that a for loop evaluates the for condition repeatedly, which duplicates while and fails to match the behavior of most other languages which implement a for loop.

Muaahahahaa!

I like the basic (pun intented) assertions of the reviewer:

  • Kernighan's mistakes
  • infamous fact
  • fails to match the behavior of most other languages

For me, it smells of someone who never succeeded mastering C's basic features/philosphy.

Introduction

As I was still studying physics in university, I found C (i.e. C-like languages) were to become my language of choice when I discovered C's for loop.

So apparently, one's style failure is another's style success. This will end the subjective part of this discussion.

Perhaps Kernigan's for should have been called loop?

Just kidding? Perhaps not.

The author of the review apparently decided that each language construct should have the same behavior across languages. So renaming for as loop would have eased his/her discomfort.

The problem is that the author fails to understand that C's for is an extension of while, not a different loop. This means while is a syntactic sugar light version of for, instead of the while other languages where for may have been castrated down.

Is C for really needed?

Quoting the author of the review, he/she makes the following assertions about for and while:

  • for is for constant loops, from a beginning to an end
  • while is for loops evaluating a condition at each iteration

Having orthogonal features is a good thing when you can combine them. But in all languages I know, there is no way to combine both for and while together.

Example: What if, for the reviewer's language, you need a loop going from a beginning to an end (like a for), but still able to evaluate at each loop iteration (like a while): You could need to find in a subset of a container (not the whole container), a value according to some stateful test?

In a C-like language, you use the for. In the reviewer's ideal language, you just, well, hack some ugly code, crying because you don't have a for_while loop (or C's for loop).

Conclusion

I believe we can summarize the reviewer's critic of C (and C-like languages) with the following statement "Kernigan's infamous mistake of not giving C the syntax of the previous, existing languages", which can be summarized again as "Never think different".

Quoting Bjarne Stroustrup:

There are only two kinds of languages: the ones people complain about and the ones nobody uses.

So, all in all, the reviewer's comment should be considered as a praise.

^_^

似最初 2024-07-14 23:28:29

也许 Knuth 指的是 BASIC。

在 BASIC(较旧的,我不了解 VB)中,FOR 循环的实现方式不同。

例如循环十次

FOR N=1 TO 10

...

NEXT N

---- OR ----
求 100 以下的偶数之和

FOR N=0 TO 100 STEP 2

...

NEXT N

在 C 语言中,“for”中可能有任何条件来检查是否退出循环。 我认为更加灵活。

Maybe Knuth is referring to BASIC.

In BASIC (the older one, I dont know about VB), FOR loops were implemented differently.

E.g. to loop ten times

FOR N=1 TO 10

...

NEXT N

---- OR ----
to find sum of even numbers below 100

FOR N=0 TO 100 STEP 2

...

NEXT N

In C, you may have any condition in "for" to check for exiting the loop. More flexible, I think.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文