7

Pascalがそのように設計されたためですか、それともトレードオフがありますか?

または、for-block内のカウンターの変更を禁止または禁止しないことの長所と短所は何ですか?私見ですが、for-block内のカウンターを変更することはほとんどありません。

編集
for-block内のカウンターを変更する必要がある1つの例を提供できますか?

両方の答えがとても良いので、wallykの答えとcartoonfoxの答えのどちらかを選ぶのは難しいです。Cartoonfoxは言語の側面から問題を分析しますが、wallykは歴史と現実の側面から問題を分析します。とにかく、あなたのすべての答えに感謝しますそして、wallykに特に感謝します。

4

7 に答える 7

14

プログラミング言語理論(および計算可能性理論)では、WHILEループとFORループの理論的特性は異なります

  • WHILEループは決して終了しない可能性があります(式はTRUEである可能性があります)
  • FORループが実行される有限の回数は、実行を開始する前にわかっているはずです。FORループは常に終了することを知っているはずです。

Cに存在するFORループは、ループを実行する前にループが何回繰り返されるかが必ずしもわからないため、技術的にはFORループとしてカウントされません。(つまり、ループカウンターをハックして永久に実行できます)

WHILEループで解決できる問題のクラスは、Pascalにある厳密なFORループで解決できる問題よりも厳密に強力です。

Pascalはこのように設計されているため、学生は異なる計算プロパティを持つ2つの異なるループ構造を持ちます。(FOR C-wayを実装した場合、FORループはwhileの代替構文になります...)

厳密に理論的には、forループ内のカウンターを変更する必要はありません。それを回避できれば、WHILEループの代替構文があります。

「whileループの計算可能性」と「forループの計算可能性」について詳しくは、次のCSレクチャーノートをご覧ください。http ://www-compsci.swan.ac.uk/~csjvt/JVTTeaching/TPL.html

もう1つのそのようなプロパティは、forループの後にloopvariableが未定義であるということです。これにより、最適化も容易になります

于 2010-01-20T01:45:26.237 に答える
12

Pascalは、1960年代と1970年代のメインフレームであるCDC Cyber​​に最初に実装されました。これは、今日の多くのCPUと同様に、優れたシーケンシャル命令実行パフォーマンスを備えていますが、ブランチのパフォーマンスが大幅に低下します。サイバーアーキテクチャのこの特性およびその他の特性は、おそらくPascalのforループの設計に大きな影響を与えました。

簡単に言うと、ループ変数の割り当てを許可するには、追加のガードコードが必要であり、通常は18ビットインデックスレジスタで適切に処理できるループ変数の最適化が台無しになります。当時、ソフトウェアのパフォーマンスは、ハードウェアの費用と他の方法で高速化できないために高く評価されていました。

長い答え

サイバーを含むControlDataCorporation 6600ファミリは、18ビットアドレスによって参照される60ビットの中央メモリワードを使用するRISCアーキテクチャです。一部のモデルには、6ビット文字フィールドを直接アドレス指定するための(高価な、したがって一般的ではない)オプションであるCompare-Move Unit(CMU)がありましたが、それ以外の場合は、いかなる種類の「バイト」もサポートされませんでした。CMUは一般的に信頼できないため、ほとんどのサイバーコードはCMUがないために生成されました。小文字のサポートが暫定的な12ビット文字表現に取って代わられるまで、単語あたり10文字が通常のデータ形式でした。

命令は15ビットまたは30ビット長ですが、CMU命令は実質的に60ビット長です。したがって、各ワードに最大4つの命令、または2つの30ビット、または15ビットと1つの30ビットのペアがパックされます。30ビット命令はワードにまたがることはできません。分岐先は単語のみを参照できるため、ジャンプターゲットは単語に合わせて配置されます。

アーキテクチャにはスタックがありません。実際、プロシージャ呼び出し命令RJは本質的に再入可能ではありません。 RJ命令がある場所の後に次の命令へのジャンプをRJ書き込むことにより、呼び出されたプロシージャの最初のワードを変更します。呼び出されたプロシージャは、リターンリンケージ用に予約されている先頭にジャンプして、呼び出し元に戻ります。手順は2番目の単語から始まります。再帰を実装するために、ほとんどのコンパイラはヘルパー関数を利用していました。

レジスタファイルには、アドレス操作用のA0..A7、インデックス作成用のB0..B7、および一般演算用のX0..X7の3種類のレジスタのそれぞれ8つのインスタンスがあります。AおよびBレジスタは18ビットです。Xレジスタは60ビットです。A1からA5を設定すると、対応するX1からX5レジスタにロードされたアドレスの内容がロードされるという副作用があります。A6またはA7を設定すると、対応するX6またはX7の内容がAレジスタにロードされたアドレスに書き込まれます。A0とX0は接続されていません。Bレジスタは、他のA、B、またはXレジスタに加算または減算する値として、事実上すべての命令で使用できます。したがって、それらは小さなカウンターに最適です。

効率的なコードの場合、ループ変数には直接比較命令を使用できるため(B2 <100など)、Bレジスタがループ変数に使用されます。Xレジスタとの比較はゼロとの関係に制限されるため、たとえばXレジスタを100と比較するには、100を減算し、結果をゼロ未満でテストする必要があります。ループ変数への割り当てが許可されている場合、60ビット値Bレジスタに割り当てる前に、範囲をチェックする必要があります。これは本当に面倒です。Herr Wirthはおそらく、面倒で非効率なことの両方が実用性に値しないと考えました。プログラマーは、その状況で常にwhileまたはrepeat...untilループを使用できます。

追加の奇妙さ

いくつかのPascal独自の言語機能は、Cyber​​の側面に直接関連しています。

  • packキーワード:単一の「文字」が60ビットの単語を消費するか、単語ごとに10文字がパックされます。
  • (珍しい)alfaタイプ:packed array [1..10] of char
  • 固有の手順pack()unpack()パックされた文字を処理します。これらは、最新のアーキテクチャでは変換を実行せず、型変換のみを実行します。
  • textファイルの奇妙さ対。file of char
  • 明示的な改行文字はありません。レコード管理は明示的に呼び出されましたwriteln
  • CDCでは非常に便利でしたが、メモリをset of char過剰に使用するため(8ビットASCIIの場合は32バイト変数/定数)、後続の多くの8ビットマシンではサポートされませんでした。対照的に、単一のサイバーワードは、改行などを省略して、ネイティブの62文字セットを管理できます。
  • 完全な式の評価(ショートカットではなく)。これらは、ジャンプして1または0に設定することによって(今日のほとんどのコードジェネレーターが行うように)ではなく、ブール演算を実装するCPU命令を使用することによって実装されました。
于 2010-01-20T07:03:55.677 に答える
7

Pascalは元々、ブロック構造化プログラミングを促進するための教育言語として設計されました。Kernighan(K&RのK)は、Pascalの制限について(当然のことながら偏った)エッセイを書きました。なぜPascalは私のお気に入りのプログラミング言語ではないのですか。

Pascalがループの制御変数と呼ぶものを変更することを禁止しfor、ステートメントがないことと組み合わせてbreak、ループ本体がその内容を調べなくても何回実行されるかを知ることができることを意味します。

ステートメントがなくbreak、ループの終了後に制御変数を使用できないことは、一部の文字列および配列処理アルゴリズムが「明らかな」に記述されないため、ループ内の制御変数を変更できないことよりも制限があります。 " 仕方。

PascalとCのこれらおよびその他の違いは、最初に設計された哲学の違いを反映しています。Pascalは「正しい」設計の概念を適用し、Cは多かれ少なかれ危険なものであっても何でも許可します。

(注:Delphiには、Cと同様に、Breakステートメントもあります。)ContinueExitreturn

ループを使用していつでも書き換えることができるため、ループ内で制御変数を変更できる必要がないことは明らかです。このような動作が使用されるCの例は、K&Rセクション7.3にあります。ここでは、の単純なバージョンが紹介されています。フォーマット文字列内のシーケンスを処理するコードは次のとおりです。forwhileprintf()'%'fmt

for (p = fmt; *p; p++) {
    if (*p != '%') {
        putchar(*p);
        continue;
    }
    switch (*++p) {
    case 'd':
        /* handle integers */
        break;
    case 'f':
        /* handle floats */
        break;
    case 's':
        /* handle strings */
        break;
    default:
        putchar(*p);
        break;
    }
}

これはループ変数としてポインターを使用しますが、整数インデックスを使用して文字列に書き込むこともできます。

for (i = 0; i < strlen(fmt); i++) {
    if (fmt[i] != '%') {
        putchar(fmt[i]);
        continue;
    }
    switch (fmt[++i]) {
    case 'd':
        /* handle integers */
        break;
    case 'f':
        /* handle floats */
        break;
    case 's':
        /* handle strings */
        break;
    default:
        putchar(fmt[i]);
        break;
    }
}
于 2010-01-17T17:50:25.313 に答える
3

いくつかの最適化(たとえば、ループの展開)を簡単にすることができます。ループの動作が予測可能かどうかを判断するための複雑な静的分析は必要ありません。

于 2010-01-16T03:13:31.837 に答える
1

Forループから

一部の言語(CまたはC ++ではない)では、ループ変数はループ本体のスコープ内で不変であり、その値を変更しようとすると、セマンティックエラーと見なされます。このような変更は、プログラマーエラーの結果である場合があり、一度実行すると特定が非常に困難になる可能性があります。ただし、コンパイラによって検出される可能性が高いのは、明白な変更のみです。ループ変数のアドレスがサブルーチンへの引数として渡される状況では、ルーチンの動作は一般にコンパイラーに認識されないため、チェックが非常に困難になります。

ですから、これは後で手をやけどしないようにするのに役立つようです。

于 2010-01-15T13:45:15.740 に答える
1

免責事項:最後にPASCALを実行してから数十年が経過しているため、構文が正確に正しくない可能性があります。

PASCALはNicklausWirthの子であり、WirthはPASCAL(およびその後継のすべて)を設計する際に信頼性と理解しやすさを非常に重視していたことを覚えておく必要があります。

次のコードフラグメントについて考えてみます。

FOR I := 1 TO 42 (* THE UNIVERSAL ANSWER *) DO FOO(I);

手順FOOを見ずに、次の質問に答えてください。このループは終了しますか?どうして知っていますか?プロシージャFOOはループで何回呼び出されますか?どうして知っていますか?

PASCALは、ループ本体のインデックス変数を変更することを禁じています。これにより、これらの質問に対する回答を知ることができ、プロシージャFOOが変更されても回答が変更されないことがわかります。

于 2010-01-17T16:03:03.560 に答える
1

Pascalは、ループ内のforループインデックスの変更を防ぐように設計されていると結論付けるのはおそらく安全です。Pascalがプログラマーがこれを行うのを妨げる唯一の言語ではないことは注目に値します。Fortranは別の例です。

そのように言語を設計する理由は2つあります。

  1. プログラム、特にその中のforループは、理解しやすく、したがって、作成、変更、および検証が容易です。
  2. ループに入る前にループを通過するトリップカウントが確立され、その後は不変であることがコンパイラーにわかっている場合、ループの最適化は簡単です。

多くのアルゴリズムでは、この動作が必須の動作です。たとえば、配列内のすべての要素を更新します。メモリが機能する場合、Pascalはdo-whileループとrepeat-untilループも提供します。おそらく、ループインデックス変数に変更を加えたり、ループから抜け出したりしてCスタイルの言語で実装されるアルゴリズムは、これらの代替形式のループでも同じように簡単に実装できます。

私は頭をかいて、ループ内のループインデックス変数の変更を許可する説得力のある理由を見つけることができませんでしたが、それは常に悪い設計であり、正しいループ構造を要素として選択することと見なしていました良いデザインの。

よろしく

マーク

于 2010-01-17T18:42:01.177 に答える