Liegt es daran, dass Pascal so entworfen wurde, oder gibt es irgendwelche Kompromisse?
Oder was sind die Vor- und Nachteile, um die Änderung des Zählers in einem For-Block zu verbieten oder nicht zu verbieten? IMHO, es ist wenig sinnvoll, den Zähler in einem For-Block zu ändern.
BEARBEITEN :
Könnten Sie ein Beispiel angeben, bei dem wir den Zähler im For-Block ändern müssen?
Es ist schwer zwischen wlikks Antwort und cartoonfox's Antwort zu wählen, da beide Antworten so nett sind. Cartoonfox analysiert das Problem vom Sprachaspekt aus, während Wallyk das Problem aus der Geschichte und der realen Welt analysiert aspect.Anyway, danke für all deine Antworten und ich möchte mich ganz herzlich bei Wallyk bedanken.
In der Programmiersprachentheorie (und in der Berechenbarkeitstheorie) WHILE- und FOR-Schleifen haben unterschiedliche theoretische Eigenschaften :
Die in C vorhandene FOR-Schleife zählt technisch nicht als FOR-Schleife, weil Sie nicht unbedingt wissen, wie oft die Schleife iteriert, bevor sie ausgeführt wird. (d. h. Sie können den Schleifenzähler hacken, um ihn für immer auszuführen)
Die Klasse von Problemen, die Sie mit WHILE-Schleifen lösen können, ist strikt leistungsfähiger als die, die Sie mit der strikten FOR-Schleife in Pascal hätten lösen können.
Pascal ist so konzipiert, dass die Schüler zwei verschiedene Schleifenkonstrukte mit unterschiedlichen Recheneigenschaften haben . (Wenn Sie FOR für den C-Weg implementiert haben, wäre die FOR-Schleife nur eine alternative Syntax für while ...)
In streng theoretischen Begriffen sollten Sie den Zähler nie innerhalb einer for-Schleife ändern müssen. Wenn Sie damit durchkommen könnten, hätten Sie nur eine alternative Syntax für eine WHILE-Schleife .
In diesen CS-Skripten können Sie mehr über "while loop computability" und "for loop computability" erfahren: Ссылка
Eine weitere Eigenschaft ist, dass die Schleifenvariable nach der for-Schleife undefiniert ist. Dies erleichtert auch die Optimierung
Pascal wurde zuerst für den CDC Cyber implementiert - ein Mainframe der 1960er und 1970er Jahre - der, wie viele CPUs heute, eine ausgezeichnete sequenzielle Befehlsausführung, aber auch eine erhebliche Leistungseinbuße für Zweigstellen hatte. Diese und andere Eigenschaften der Cyber-Architektur haben wahrscheinlich Pascals Design von for
loops stark beeinflusst.
Die Kurze Antwort bedeutet, dass die Zuweisung einer Schleifenvariablen einen zusätzlichen Schutzcode und eine fehlerhafte Optimierung für Schleifenvariablen erfordert, die normalerweise in 18-Bit-Indexregistern gut verarbeitet werden können. Zu dieser Zeit wurde die Software-Performance wegen der Kosten der Hardware und der Unfähigkeit, sie auf andere Weise zu beschleunigen, sehr geschätzt.
Lange Antwort
Die Control Data Corporation 6600-Familie, die den Cyber enthält, ist eine RISC-Architektur, die 60-Bit-Zentralspeicherwörter verwendet, auf die 18-Bit-Adressen verweisen. Einige Modelle hatten eine (teure, daher unübliche) Option, die Compare-Move-Unit (CMU) zum direkten Adressieren von 6-Bit-Zeichenfeldern, aber ansonsten gab es keine Unterstützung für "Bytes" jeglicher Art. Da die CMU im Allgemeinen nicht gezählt werden konnte, wurde der meiste Cyber-Code für seine Abwesenheit generiert. Zehn Zeichen pro Wort waren das übliche Datenformat, bis die Unterstützung für Kleinbuchstaben einer vorläufigen 12-Bit-Zeichendarstellung wich.
Die Befehle sind 15 Bits oder 30 Bits lang, außer dass die CMU-Befehle effektiv 60 Bits lang sind. Also bis zu 4 Befehle in jedes Wort gepackt, oder zwei 30 Bit, oder ein Paar 15 Bit und ein 30 Bit. 30-Bit-Befehle können keine Wörter umfassen. Da Verzweigungsziele nur Wörter referenzieren können, sind Sprungziele wortausgerichtet.
Die Architektur hat keinen Stapel. Tatsächlich ist die Prozeduraufrufanweisung RJ
intrinsisch nicht einspringend. RJ
modifiziert das erste Wort der aufgerufenen Prozedur, indem ein Sprung zur nächsten Anweisung nach der Anweisung von RJ geschrieben wird. Aufgerufene Prozeduren kehren zum Aufrufer zurück, indem sie an ihren Anfang springen, der für die Rückkehrverknüpfung reserviert ist. Die Prozeduren beginnen mit dem zweiten Wort. Um die Rekursion zu implementieren, verwendeten die meisten Compiler eine Hilfsfunktion.
Die Registerdatei hat acht Instanzen von jeweils drei Arten von Registern, A0..A7 für die Adressenmanipulation, B0..B7 für die Indexierung und X0..X7 für die allgemeine Arithmetik. A- und B-Register sind 18 Bits; X-Register sind 60 Bits. Die Einstellung A1 bis A5 hat den Nebeneffekt, dass das entsprechende Register X1 bis X5 mit dem Inhalt der geladenen Adresse geladen wird. Setzen von A6 oder A7 schreibt die entsprechenden X6- oder X7-Inhalte in die Adresse, die in das A-Register geladen ist. A0 und X0 sind nicht verbunden. Die B-Register können in praktisch jedem Befehl als ein Wert zum Addieren oder Subtrahieren von irgendeinem anderen A-, B- oder X-Register verwendet werden. Daher sind sie ideal für kleine Zähler.
Für einen effizienten Code wird ein B-Register für Schleifenvariablen verwendet, da direkte Vergleichsbefehle an ihnen verwendet werden können (B2 & lt; 100 usw.); Vergleiche mit X-Registern sind auf Relationen zu Null beschränkt, so dass ein Vergleich eines X-Registers mit 100 beispielsweise das Subtrahieren von 100 und das Testen des Ergebnisses für weniger als Null usw. erfordert. Wenn eine Zuweisung an die Schleifenvariable erlaubt war, ein 60-Bit-Wert müsste vor der Zuordnung zum B-Register range-geprüft werden. Das ist ein wirklicher Ärger. Herr Wirth hat wahrscheinlich gedacht, dass sowohl der Aufwand als auch die Ineffizienz den Nutzen nicht wert sind - der Programmierer kann immer eine while
oder repeat
... until
Schleife in dieser Situation verwenden.
Zusätzliche Seltsamkeit
Mehrere Funktionen von Unique-to-Pascal beziehen sich direkt auf Aspekte des Cyber:
pack
: Entweder ein einzelnes "Zeichen" verbraucht ein 60-Bit-Wort oder es sind zehn Zeichen pro Wort gepackt. alfa
Typ: packed array [1..10] of char
pack()
und unpack()
, um mit gepackten Zeichen umzugehen. Diese führen keine Transformation auf modernen Architekturen durch, nur Typumwandlung. text
files vs file of char
writeln
aufgerufen
set of char
sehr nützlich für CDCs war, wurde es auf vielen nachfolgenden 8-Bit-Maschinen aufgrund seiner übermäßigen Speicherbelegung (32-Byte-Variablen / Konstanten für 8-Bit-ASCII) nicht unterstützt. Im Gegensatz dazu könnte ein einzelnes Cyber-Wort den nativen 62-Zeichen-Satz verwalten, indem Newline und etwas anderes weggelassen wird. Pascal wurde ursprünglich als Unterrichtssprache entwickelt, um blockorientierte Programmierung zu fördern. Kernighan (das K von K & amp; R) schrieb einen (verständlicherweise voreingenommenen) Aufsatz über Pascals Beschränkungen, Warum Pascal nicht meine bevorzugte Programmiersprache ist .
Das Verbot, das zu ändern, was Pascal die Steuervariable einer for
-Schleife nennt, kombiniert mit dem Fehlen einer break
-Anweisung bedeutet, dass es möglich ist zu wissen, wie oft der Schleifenkörper ist wird ausgeführt, ohne seinen Inhalt zu studieren.
Ohne eine break
-Anweisung, und nicht in der Lage, die Steuervariable zu verwenden, nachdem die Schleife beendet wird, ist mehr eine Einschränkung, als die Steuervariable innerhalb der Schleife nicht ändern zu können, da einige String- und Array-Verarbeitungsalgorithmen davon abgehalten werden auf die "offensichtliche" Weise geschrieben werden.
Diese und andere Unterschiede zwischen Pascal und C spiegeln die verschiedenen Philosophien wider, mit denen sie ursprünglich entworfen wurden: Pascal, um ein Konzept des "richtigen" Designs durchzusetzen, C um mehr oder weniger alles zu erlauben, egal wie gefährlich es ist.
(Hinweis: Delphi hat jedoch eine Break
-Anweisung sowie Continue
und Exit
, was wie return
in C ist.)
Natürlich brauchen wir nicht , um die Steuervariable in einer for
-Schleife ändern zu können, weil wir immer mit einer while
-Schleife umschreiben können. Ein Beispiel in C, wo solch ein Verhalten verwendet wird, kann in K & amp; R Abschnitt 7.3 gefunden werden, wo eine einfache Version von printf()
eingeführt wird. Der Code, der '%'
Sequenzen innerhalb einer Formatzeichenfolge fmt
verarbeitet, ist:
Obwohl ein Zeiger als Schleifenvariable verwendet wird, könnte er auch mit einem ganzzahligen Index in die Zeichenfolge geschrieben worden sein:
%Vor%Von For-Schleife
In einigen Sprachen (nicht C oder C ++) Schleifenvariable ist innerhalb der Umfang des Schleifenkörpers, mit irgendwelchen versuchen, seinen Wert zu ändern als semantischer Fehler betrachtet. Eine solche Modifikationen sind manchmal a Folge eines Programmierfehlers, Das kann sehr schwierig sein identifiziere dich einmal. Jedoch nur offen Änderungen werden wahrscheinlich von entdeckt der Compiler. Situationen, in denen die Adresse der Schleifenvariablen wird übergeben als ein Argument zu einer Subroutine machen es sehr schwer zu überprüfen, weil die Das Verhalten der Routine ist im Allgemeinen Unerkennbar für den Compiler.
Das scheint also zu sein, damit Sie sich später nicht die Hand verbrennen.
Haftungsausschluss: Es ist Jahrzehnte her, dass ich PASCAL zum letzten Mal benutzt habe, daher ist meine Syntax möglicherweise nicht genau richtig.
Sie müssen sich daran erinnern, dass PASCAL das Kind von Nicklaus Wirth ist, und Wirth sorgte sich sehr stark um Zuverlässigkeit und Verständlichkeit, als er PASCAL (und all seine Nachfolger) entwarf.
Betrachten Sie das folgende Codefragment:
%Vor%Beantworten Sie diese Fragen, ohne die Prozedur FOO zu betrachten: Wird diese Schleife jemals enden? Woher weißt du das? Wie oft wird Prozedur FOO in der Schleife aufgerufen? Woher weißt du das?
PASCAL verbietet das Ändern der Indexvariablen im Schleifenkörper, so dass es möglich ist, die Antworten auf diese Fragen zu kennen, und wissen, dass sich die Antworten nicht ändern werden, wenn sich die Prozedur FOO ändert.
Es ist wahrscheinlich sicher, dass Pascal entworfen wurde, um die Modifikation eines for-Schleife-Index innerhalb der Schleife zu verhindern. Es ist erwähnenswert, dass Pascal keineswegs die einzige Programmiersprache ist, die Programmierer daran hindert, Fortran ist ein anderes Beispiel.
Es gibt zwei zwingende Gründe, eine Sprache so zu gestalten:
Für viele Algorithmen ist dieses Verhalten das erforderliche Verhalten; Aktualisieren aller Elemente in einem Array zum Beispiel. Wenn Speicher dient Pascal bietet auch Do-While-Loops und Wiederholungs-Loops. Die meisten, ich denke, Algorithmen, die in C-Stil-Sprachen mit Änderungen an der Loop-Index-Variable implementiert sind oder aus der Schleife ausbrechen, könnten genauso leicht mit diesen alternativen Formen der Schleife implementiert werden.
Ich habe mir den Kopf zerkratzt und keinen zwingenden Grund gefunden, eine Schleifenindexvariable innerhalb der Schleife zu ändern, aber ich habe das immer als schlechtes Design und die Wahl des richtigen Schleifenkonstrukts betrachtet als ein Element des guten Designs.
Grüße
Markieren
Tags und Links for-loop pascal loop-counter