13

Knuth の「The Art of Computer Programming」のレビューで、以下を読みました。

「非常に『実用的』ということは、将来の CS 専攻者が、C を設計する際のカーニハンの過ちを学ばなければならないことを意味します。特に、for ループが for 条件を繰り返し評価し、while を複製し、他のほとんどの言語の動作と一致しないという悪名高い事実です。 for ループを実装します。」

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

この男は何について話しているのですか?while ループの単なるシンタックス シュガーではない for ループをどのように実装できますか?

4

11 に答える 11

29

このことを考慮:

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

この場合、最終値 100 を関数呼び出しで置き換えることができます。

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

...そしてfinal_value-function は一度だけ呼び出されます。

ただし、C では次のようになります。

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

... final_value-function は、ループの反復ごとに呼び出されるため、より冗長にすることをお勧めします。

int end = final_value();
for (int i=0; i<end; ++i) // ...
于 2008-10-23T13:26:00.440 に答える
5

単純なカウント ループだけが必要な場合は、

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 ループのすべての要素がまとめられているため、より簡単に理解できます。

于 2008-10-23T14:21:37.520 に答える
4

彼はおそらくfor i:=0 to N、セットの要素を反復処理する for ループや for-each ループのようなループについて言及しています。C スタイルの for ループを持つすべての言語は、実際には C から取得したものだと思います。

于 2008-10-23T13:22:46.953 に答える
3

Magnus の意見は正しいが、ほとんどの言語 (C より前) では、条件文が終了基準 (つまり、「i が 100 に等しくなったら停止する」)であることにも注意する必要がある。C (およびほとんどのポスト C 言語) では、これは継続基準です (つまり、「i が 100 未満の間継続する」)。

于 2008-10-23T13:34:46.147 に答える
2

Ada (そして他のほとんどの Algol 派生言語と私は信じています) では、「for」ループの終了条件は、ループの開始時に一度だけ評価されます。たとえば、Ada に次のコードがあるとします。

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

ループの開始時に q が 10 だったので、このループはちょうど 10 回繰り返されます。ただし、一見同等のループを C で記述すると、次のようになります。

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

その後、ループは 20 回繰り返されます。これは、q が十分に大きくなるまでに q が 20 に変更されたためです。

もちろん、C の方法はより柔軟です。ただし、これにはかなりのマイナスの影響があります。明らかなことは、プログラムはサイクルごとにループ条件を再チェックする労力を浪費しなければならないということです。優れたオプティマイザーは、このような単純なケースで問題を回避するのに十分賢いかもしれませんが、「q」がグローバルで、「何かを行う」にプロシージャー呼び出しが含まれている場合 (理論的には q を変更できる場合) はどうなるでしょうか?

難しい事実は、C ループよりも Ada ループについてはるかに多くのことを知っているということですつまり、オプティマイザーに同じレベルの知性と努力があれば、Ada はより優れた最適化を行うことができます。たとえば、Ada コンパイラは、コンテンツが何であれ、ループ全体をコンテンツの 10 個のコピーで置き換えることができることを認識しています。AC オプティマイザは、内容を調べて分析する必要があります。

これは、実際には、C 構文の設計がコンパイラを妨害する多くの方法の 1 つにすぎません。

于 2008-10-23T14:22:24.133 に答える
2

基本的に、C (および Java、JavaScript、および多くの C 派生言語)ループは、実際にはループforの構文糖衣です。while

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

これは非常に現在のイディオムであり、次と厳密に同等です。

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

(とにかくCのバージョン間で変更されたスコープの問題を分解します。)

停止条件は反復ごとに評価されます。場合によっては興味深いこともありますが、落とし穴になる可能性もあります。ソースで見たのは、C の コストのかかる関数であるi < strlen(longConstantString)ため、プログラムを遅くする主な方法です。回数は前もってわかっていても、早期に終了するために使用できるので、停止項の動的評価は便利というより面倒です: 本当に動的評価が必要な場合は、(または) を使用します。strlen
forbreakwhile () {}do {} while ()

Lua などの他の言語では、停止条件はループの初期化時に 1 回だけ評価されます。ループの動作を予測するのに役立ち、多くの場合、パフォーマンスが向上します。

于 2008-10-23T13:35:17.787 に答える
2

x86 では、while ループを作成せずにアセンブリ レベルで for ループを実行できます。ループ命令はレジスタ ecxの値を減らし、ecx が 0 より大きい場合はオペランドにジャンプし、それ以外の場合は何もしません。これは古典的な for ループです。

mov ecx, 0x00000010h
loop_start:
;loop body
loop loop_start
;end of loop
于 2008-10-23T13:38:04.097 に答える
2

おそらくループ展開?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"

他の命令は必要ないため、高速です。これは単純な例にすぎません。もっと良い例を見つけようと思います。

于 2008-10-23T13:32:47.087 に答える
1

非常に「実用性」とは、CS専攻になる予定の人が、Cの設計におけるカーニハンの間違いを学ばなければならないことを意味します。特に、forループがfor条件を繰り返し評価するという悪名高い事実は、重複し、他のほとんどの言語の動作と一致しません。 forループを実装します。

ムアハハハハ!

私はレビュアーの基本的な(しゃれを意図した)主張が好きです:

  • カーニハンの過ち
  • 悪名高い事実
  • 他のほとんどの言語の動作と一致しません

私にとって、それはCの基本的な機能/哲学を習得することに成功しなかった誰かのにおいがします。

序章

私はまだ大学で物理学を勉強していたので、Cのforループを発見したとき、C(つまりCのような言語)が私の選択言語になることに気づきました。

したがって、明らかに、ある人のスタイルの失敗は別の人のスタイルの成功です。これで、この議論の主観的な部分は終わりです。

おそらく、Kerniganのforはループと呼ばれるべきでしたか?

冗談だ?おそらくそうではありません。

レビューの作成者は、各言語構成が言語間で同じ動作をする必要があると明らかに決定しました。したがって、 as loopの名前変更すると、彼/彼女の不快感が緩和されます。

問題は、Cのforがwhileの拡張であり、別のループではないことを作成者が理解できないことです。これは、whileがforの構文シュガーライトバージョンであり、forが去勢された可能性がある他の言語ではないことを意味します

Cは本当に必要ですか?

レビューの著者を引用して、彼/彼女はforwhileについて次の主張をします:

  • forは、最初から最後までの一定のループ用です
  • whileは、各反復で条件を評価するforループです。

それらを組み合わせることができる場合、直交する特徴を持つことは良いことです。しかし、私が知っているすべての言語では、一緒に、そして一緒にいる間、両方を組み合わせる方法はありませ

例:レビュー担当者の言語で、最初から最後までループが必要であるが(forのよう)、ループの反復ごとに評価できる場合(whileのように:サブセットで検索する必要がある場合がありますコンテナ(コンテナ全体ではない)の、ステートフルテストによる値?

Cのような言語では、forを使用します。レビューアの理想的な言語では、 for_whileループ(またはCのforループ)がないため、醜いコードをハックして泣きます。

結論

レビューアのC(およびCのような言語)に対する批評家は、「Cに以前の既存の言語の構文を与えないというKerniganの悪名高い間違い」という言葉で要約できると思います。これは、「決して違うとは思わない」と要約できます。 。

Bjarne Stroustrupの引用:

言語は2種類しかありません。人々が不満を言う言語と、誰も使用しない言語です。

したがって、全体として、レビューアのコメントは賞賛と見なされるべきです。

^ _ ^

于 2008-10-24T08:28:52.020 に答える
1

これらの言語純粋主義者が決して認識していないように見えるのは、C の全体的なポイントであり、ある程度まで C++ は、必要なものを必要な方法で実装する可能性を与えているということです。確かに、最後の式の結果が変化した場合は、while ループを使用する方がよいでしょう。おそらく問題は、プログラマーが C が「ハイレベル」を実装するという印象を受けることですが、誰もが C がかなりローレベルな言語であることを知っている必要があります。

于 2008-10-23T14:45:50.093 に答える
0

たぶん、Knuth は BASIC について言及しています。

BASIC (古いもので、VB については知りません) では、FOR ループの実装が異なっていました。

例:10回ループする

N=1 ~ 10 の場合

...

ネクストN

---- または ---- 100 未満の偶数の合計を求める

N=0 ~ 100 の場合 STEP 2

...

ネクストN

C では、ループの終了をチェックする条件を「for」に含めることができます。もっと柔軟だと思います。

于 2008-10-23T14:34:46.293 に答える