In einer von mir kreierten Programmiersprache gibt es die "Schleife mit Hineinsprung" anstelle der while-Schleife. Kennt jemand sonst eine Sprache mit dieser Struktur? Beitrag "Re: 8bit-Computing mit FPGA"
In C ist das eine Do-While-Schleife mit eine Goto für den Sprung ins Innere der Schleife. Wenn man will, kann man mit ein paar Makros den Goto kaschieren und das Ganze wie eine "echte" Kontrollstruktur aussehen lassen. Wenn man die Anweisungen etwas umstellt, kann man auch eine Endlos-Schleife mit bedingtem Break verwenden. Das ist der Weg, wie so etwas üblicherweise realisiert wird.
Josef G. schrieb: > In einer von mir kreierten Programmiersprache gibt es die > "Schleife mit Hineinsprung" anstelle der while-Schleife. Das erinnert mich an den Vorschlag, komplementär zum GO TO-Befehl noch einen COME FROM-Befehl zu implementieren. > Kennt jemand sonst eine Sprache mit dieser Struktur? BASIC.
Yalu X. schrieb: > Wenn man die Anweisungen etwas umstellt, kann man auch eine > Endlos-Schleife mit bedingtem Break verwenden. Das ist der > Weg, wie so etwas üblicherweise realisiert wird. Sofern der Compiler das nicht optimiert und eine Schleife mit Hineinsprung daraus macht, hätte diese Lösung allerdings den Nachteil, dass bei jedem Durchlauf 2 Sprünge ausgeführt oder Sprungbedingungen getestet werden müssen: der unbedingte Rücksprung am Schluss und ein bedingter Sprung für break.
Jörg Wunsch schrieb: > BASIC. In meiner Sprache gibt es kein GOTO, und die Blockstruktur ist streng und kann nicht durchbrochen werden.
Meine Eingangsfrage war so gemeint, ob es eine solche Struktur in irgendeiner Sprache fertig gibt. Dass man sie mit anderen Strukturen nachbilden kann ist klar, und wenn man GOTO zulässt sowieso. Die Antwort auf die Frage lautet nach den bisherigen Reaktionen: Es gibt keine solche Sprache.
kenne ich auch nicht, bedarf scheint also nicht sehr hoch zu sein ;-)
:
Bearbeitet durch User
@Josef G. (bome) Benutzerseite >In meiner Sprache gibt es kein GOTO, und die Blockstruktur >ist streng und kann nicht durchbrochen werden. Das beschreibt dein (Kommunikations)dilema ziemlich gut!
@ Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite >Das erinnert mich an den Vorschlag, komplementär zum GO TO-Befehl >noch einen COME FROM-Befehl zu implementieren. Naja, im weitesten Sinne wäre das die Sprungmarke einer ISR ;-)
Josef G. schrieb: > Sofern der Compiler das nicht optimiert und eine Schleife mit > Hineinsprung daraus macht, hätte diese Lösung allerdings den > Nachteil, dass bei jedem Durchlauf 2 Sprünge ausgeführt oder > Sprungbedingungen getestet werden müssen: der unbedingte > Rücksprung am Schluss und ein bedingter Sprung für break. Neue Programmiersprachen werden entworfen, um den Programmierer – und nicht den Compiler – in seiner Arbeit zu unterstützen. Werden bestimmte Konstrukte nicht zufriedenstellend optimiert, wird deswegen am Compiler Hand angelegt, nicht an der Programmiersprache. Josef G. schrieb: > Meine Eingangsfrage war so gemeint, ob es eine solche Struktur > in irgendeiner Sprache fertig gibt. Nicht dass ich wüsste. In deiner neuen Kontrollstruktur steht derjenige Teil des Schleifenrumpfs, der zuerst ausgeführt wird, nach dem Teil, der danach ausgeführt wird. Dadurch wird die tatsächliche Ausführungsreihenfolge verschleiert. Das Konstrukt nimmt also dem Compiler minimal Arbeit ab, belastet aber auf der anderen Seite den Programmierer, nicht nur, weil er dafür "vekehrt" herum denken muss, sondern auch, weil er für diesen zweifelhaften Fortschritt erst eine neue Sprache lernen muss, seinen bestehenden Code ggf. umschreiben muss usw. Das ist aber nicht das Ziel moderner Programmiersprachen.
:
Bearbeitet durch Moderator
Yalu X. schrieb: > belastet aber auf der anderen Seite den Programmierer, > nicht nur, weil er dafür "vekehrt" herum denken muss, Das ist nicht richtig. Der Code steht in der Reihenfolge da, wie er ausgeführt wird, der Abbruch erfolgt am Ende. Nur beim Starten der Schleife wird einmalig ein Abschnitt am Anfang übersprungen. Und das ist kein Problem für den Programmierer, denn es steht ja am Anfang explizit da: L.JP, JP für"jump".
Jörg Wunsch schrieb: > Das erinnert mich an den Vorschlag, komplementär zum GO TO-Befehl > noch einen COME FROM-Befehl zu implementieren. der nicht so unsinnig ist wie es scheint. Am PC1500 hatte ich relokatiblen Assemblercode einige Unterprogramme im Eeprom geschrieben. Branch war leicht möglich da mein Ziel bekannte Byte vorher oder zurück lag, goto jsr jump sub routine schied ja aus wegen relokatibel. Aber wie zurück kommen wenn ich nicht weiss woher ich kam ? Deswegen musste ich tricky den PC (Programmcounter) auslesen auf den erst mal auf den Stack packen und diesen so manipulieren das er in meiner Unterroutine passend richtig liegt für ein RET (return jump)
:
Bearbeitet durch User
Josef G. schrieb: > Yalu X. schrieb: >> belastet aber auf der anderen Seite den Programmierer, >> nicht nur, weil er dafür "vekehrt" herum denken muss, > > Das ist nicht richtig. Der Code steht in der Reihenfolge da, > wie er ausgeführt wird, der Abbruch erfolgt am Ende. Nur beim > Starten der Schleife wird einmalig ein Abschnitt am Anfang > übersprungen. Kann es sein, dass du ein Programm tatsächlich in der Richtung liest, wie du es hier beschreibst, nämlich von hinten nach vorne bzw. vom Ende zum Start? Dann ist dein neues Konstrukt für dich genau richtig ;-) Bei den meisten Leuten ist das aber anders herum. Sie fragen sich: Was macht das Programm als erstes, was folgt danach?
Josef G. schrieb: > Nur beim > Starten der Schleife wird einmalig ein Abschnitt am Anfang > übersprungen. Ich überleg jetzt schon eine ganze Weile. Wahrscheinlich hatte ich in all den Jahren durchaus schon mal den Fall, der von deiner "Schleife mit Hineinsprung" abgedeckt wird. Aber im Moment fällt mir kein Beispiel aus den letzten Jahren ein, bei dem ich das mal gebraucht hätte. In normalem C wäre das ja im Grunde (wenn ich das richtig verstanden habe)
1 | xyz; |
2 | |
3 | while( Abbruchbedingung ) { |
4 | abc; |
5 | xyz; |
6 | }
|
wobei xyz bzw. abc für irgendwelche nicht triviale Codeteile stehen. Solange die Codeteile nicht zu komplex sind, könnte man das auch so schreiben
1 | for( xyz; Abbruchbedingung; xyz ) |
2 | abc; |
ich finde allerdings die erste Variante mit dem while trotzdem besser. Nur. Das ist gar nicht der springende Punkt. Ich bin jetzt sicher nicht der repräsentative Programmierer, der das Mass aller Dinge darstellt. Trotzdem. Mir fällt ad hoc kein reales Beispiel aus den letzten Jahren ein und ich bilde mir ein, vom Code der letzten Jahre noch so einiges im Gedächtnis parat zu haben. So häufig dürfte der Fall also nicht sein. Das erinnert mich irgendwie an 'Duffs Device'. Das ist ein netter kleiner Trick, aber ausser auf tiefster Treiberebene braucht das Ding im Grude genommen kein Mensch. Und selbst da ist zweifelhaft ob man das Loop-Unrolling nicht doch besser dem Compiler überlassen sollte.
:
Bearbeitet durch User
Die Frage ist vielmehr. Gibt es einen Programmierertyp, der mit dieser Schleife was anfangen kann? ;-)
Da offenbar kaum jemand den im Eröffnungsbeitrag verlinkten Beitrag im Nachbarforum anschaut, hier eine Kopie: > *L.JP / springt zu *HERE > oper1 > oper2 > *HERE > oper3 > oper4 / weist VAR einen Wert zu > *RP.Z VAR / springt zu oper1 falls VAR = zero > In C würde man hier eine function definieren, welche > oper3/4 ausführt und den Rückgabewert VAR erhält. > Und man würde eine while-Schleife verwenden, welche > oper1/2 ausführt, solange die function Null liefert. > Die Schleife mit Hineinsprung erfüllt den gleichen > Zweck auf einfachere Weise. Die Hochsprache braucht dann > auch nicht das Konstrukt einer function mit Rückgabewert, > sondern es reichen einfache Unterprogramme, bei welchen > alle Übergabe-Parameter gleichberechtigt sind. Ergänzend hierzu noch der Hinweis auf obigen Beitrag: Josef G. schrieb: > Yalu X. schrieb: >> Wenn man die Anweisungen etwas umstellt, kann man auch eine >> Endlos-Schleife mit bedingtem Break verwenden. Das ist der >> Weg, wie so etwas üblicherweise realisiert wird. > > Sofern der Compiler das nicht optimiert und eine Schleife mit > Hineinsprung daraus macht, hätte diese Lösung allerdings den > Nachteil, dass bei jedem Durchlauf 2 Sprünge ausgeführt oder > Sprungbedingungen getestet werden müssen: der unbedingte > Rücksprung am Schluss und ein bedingter Sprung für break.
Josef G. schrieb: >> Die Schleife mit Hineinsprung erfüllt den gleichen >> Zweck auf einfachere Weise. Das ist doch gar nicht der springende Punkt. Der springende Punkt ist, dass diese Form der Schleife so selten vorkommt, dass es sich überhaupt nicht lohnt, dafür ein extra Sprachkonstrukt aufzusetzen. Du tust so, als ob das ein enorm drängendes Problem wäre. Das ist es nicht. Deshalb bricht hier auch keiner in Jubelstürme aus. (Allerdings: warst du nicht der Typ, der einen eigenen Zeichensatz vorgeschlagen hat, der für Hex-Ziffern eigene Zeichen benutzt? Selbiges: Du schlägst eine Lösung für etwas vor, was überhaupt kein Problem ist)
:
Bearbeitet durch User
Karl Heinz schrieb: > Der springende Punkt ist, dass diese > Form der Schleife so selten vorkommt Wie ich im Eröffnungsbeitrag geschrieben habe, gibt es diese Schleife bei meiner Sprache nicht zusätzlich zur while-Schleife, sondern anstelle der while-Schleife. Deshalb kommt sie häufig vor. Und die hier beschriebenen Vorteile kommen dann hinzu.
Jörg Wunsch schrieb: > Das erinnert mich an den Vorschlag, komplementär zum GO TO-Befehl > noch einen COME FROM-Befehl zu implementieren. Ein COMEFROM käme wohl noch am ehesten dem EXCEPT eines TRY/EXCEPT Konstrukts nahe.
Josef G. schrieb: > In einer von mir kreierten Programmiersprache gibt es die > "Schleife mit Hineinsprung" anstelle der while-Schleife. Das erinnert mich ein wenig an den hier: Ein Betrunkener tastet sich irritiert ständig um eine Litfassäule herum. Nach etwa 10 Runden sinkt er resigniert in sich zusammen und murmelt: "Verdammt! Eingesperrt!" Und genau das kann passiert bei dieser Hineinsprung-Schleife auch: es ist eine Frage der Sichtweise. Man stülpt sie einfach um und hat einen definierten Anfang, mittendrin einen Absprung und sonst eine einfache Schleife...
@ Karl Heinz (kbuchegg) (Moderator) >Das ist doch gar nicht der springende Punkt. In der Tat. >(Allerdings: warst du nicht der Typ, der einen eigenen Zeichensatz >vorgeschlagen hat, der für Hex-Ziffern eigene Zeichen benutzt? >Selbiges: Du schlägst eine Lösung für etwas vor, was überhaupt kein >Problem ist) Du hast es erfasst. Ich verweise auf Beitrag "wer kann sich noch an den hex-zeichensatz erinnern?"
Wie sieht eigentlich diese neue Programmiersprache insgesamt aus? Gibt es da eine Spezifikation oder Beispielprogramme? Gibt es neben der Schleife mit "Hineinsprung" noch weitere Features, die sie von bestehenden Sprachen unterscheiden? Noch etwas zum Thema Schleifenoptimierung: Der GCC dreht alle Schleifen – egal, ob for, while, do-while oder endlos mit break – so hin, dass die Abbruchbedingung am Ende liegt. Man muss sich darum als Programmierer also keine Gedanken machen.
Yalu X. schrieb: > Gibt es da eine Spezifikation oder Beispielprogramme? Siehe meine Benutzerseite, da ist meine Website zu finden. Die Sprache wird beschrieben auf der Seite SYS-doku. Zum Testen auf Linux-PC gibt es ein Emulationsprogramm des 8bit-Rechners, und Beispielprogramme. > weitere Features, die sie von bestehenden Sprachen unterscheiden? Die Sprache ist angepasst an die Hardware des 8bit-Rechners. Sie stellt Variablen und Strukturelemente (zB. Schleifen) bereit, enthält selber aber nur ganz wenige Routinen (zB. Inkrementieren/Dekrementieren). Der Hauptteil der Routinen wird von den 8 Steckkarten geliefert. Ein Schwachpunkt der Sprache: Sie kennt keine Ausdrücke. Stattdessen gibt es "Mikrooperationen", welche ebenfalls von den Steckkarten geliefert werden. Statt VAR1 = VAR2 + VAR3 würde es zB. heissen GET VAR2 ADD VAR3 STO VAR1 je nach Steckkarten-Software. Der Compiler erzeugt keinen Maschinencode, sondern einen Code, welcher interpretativ von der CPU abgearbeitet wird. Sofern die Steckkarten-Software dafür ausgelegt ist, kann man die Zahl der Taktzyklen für alle Programmteile exakt berechnen.
Josef G. schrieb: > Die Sprache ist angepasst an die Hardware des 8bit-Rechners. Josef G. schrieb: > Ein Schwachpunkt der Sprache: Sie kennt keine Ausdrücke. Stattdessen > gibt es "Mikrooperationen", welche ebenfalls von den Steckkarten > geliefert werden. Statt VAR1 = VAR2 + VAR3 würde es zB. heissen > GET VAR2 ADD VAR3 STO VAR1 je nach Steckkarten-Software. Josef G. schrieb: > In der im ROM eingebauten Hochsprache des Gesamtsystems > wird nach gleichem Muster die "Schleife mit Hineinsprung" > zur Realisierung von while-Schleifen verwendet. Jetzt bleibt nur noch die Frage, was man unter einer Hochsprache versteht.
Kleiner Hinweis für alle, die sich dem COME-FROM-Thema mit einer gewissen Ernsthaftigkeit genähert haben: Seht Euch mal die Spezifikation zu INTERCAL an, das ist die Sprache, aus der das Konstrukt stammt. So was kommt heraus, wenn ein paar wirklich kluge Leute wie ESR eine Spaßsprache entwickeln - kein Vergleich zu diesen kreuzlangweiligen Turingmaschinen-Emulatoren mit pseudolustigen Symbolen wie brainfuck, Ook!, Whitespace etc. http://catb.org/esr/intercal/ http://catb.org/esr/intercal/ick.htm
Klaus Wachtler schrieb: > Jetzt bleibt nur noch die Frage, was man unter einer Hochsprache > versteht. Dieses Ding, was Josef im Nachbarthread Beitrag "Re: 8bit-Computing mit FPGA" (1) unbeirrt anpreist, obwohl es keiner haben will, ist jedenfalls keine Hochsprache. Ich würde es "kryptischen Assembler" nennen, wobei die Betonung auf kryptisch liegt. Seine Mnemonics bieten dem Leser leider überhaupt keine Eselsbrücken an, um Assoziationen zu bereits bekannten Befehlen zu ziehen. Kurz: Es ist für mich genauso unlesbar wie chinesisch. (1) Link zeigt den vollständigen Befehlssatz.
:
Bearbeitet durch Moderator
Josef G. schrieb: > Jörg Wunsch schrieb: >> BASIC. > > In meiner Sprache gibt es kein GOTO, und die Blockstruktur > ist streng und kann nicht durchbrochen werden. Blöcke? dazu ne Frage.. gibt es diese "Schleife mit Hineinspringen" jetzt eigentlich shon oder ist das noch eher theoretsich? der syntax lässt ja mal keine "blöcke" erkennen, verschachteln scheint mir unmöglich..
Robert L. schrieb: > oder ist das noch eher theoretsich? Es gibt eine fertige Implementierung auf meinem 8bit-Rechner > der syntax lässt ja mal keine "blöcke" erkennen, Der Bereich zwischen *L.JP und *HERE zählt als ein Block, ebenso der Bereich zwischen *HERE und *RP.Z > verschachteln scheint mir unmöglich.. Doch, ist natürlich möglich.
Frank M. schrieb: > ist jedenfalls keine Hochsprache. Was du verlinkt hast ist die Tabelle der Assembler- Mnemonics und hat mit der Hochsprache nichts zu tun.
@Josef G. (bome) Benutzerseite >> ist jedenfalls keine Hochsprache. >Was du verlinkt hast ist die Tabelle der Assembler- >Mnemonics und hat mit der Hochsprache nichts zu tun. Was du da fabriziert hast ist eher eine Art Anti-Logikpuzzle und hat mit einem ANWENDBAREN Mikrorechnersystem nichts zu tun. :-0
Josef G. schrieb: > Frank M. schrieb: >> ist jedenfalls keine Hochsprache. > > Was du verlinkt hast ist die Tabelle der Assembler- > Mnemonics und hat mit der Hochsprache nichts zu tun. Die Verwechslungsgefahr zwischen den Assemblerm-Mnemonics und den Hochsprachenschlüsselwörtern kommt aber auch nicht ganz von ungefähr: *L.JP und *RP.Z sind Hochsprache, die nicht viel anders aussehenden Befehle LZ.A und S.RP sind Assembler. Beim folgenden Code dachte ich auch erst, dass das ja nur der vom Compiler aus der Hininspringschleife generierte Assemblercode sein kann: > *L.JP / springt zu *HERE > oper1 > oper2 > *HERE > oper3 > oper4 / weist VAR einen Wert zu > *RP.Z VAR / springt zu oper1 falls VAR = zero Ist es aber nicht, wie ich inzwischen gelernt habe. Ich habe auch schon versucht, die Dokmentation auf deiner Webseite zu verstehen. Bei den einzelnen Seiten (home, CPU-doku, SYS-doku, Emul und Hawa) schmeißt du aber schon in den ersten 10 Zeilen mit Bergiffen und Konzepten um dich, dass ich dachte, ich müsste erst eine andere Seite durcharbeiten, um die Grundlagen zu verstehen. Dieses Problem hatte ich aber auf jeder der fünf Seiten, so dass sich sozusagen eine fünfschwänzige Katze jeweils fünffach in ihre fünf Schwänze biss. Deine Hochsprache (oder das, was ich meine, dass sie es ist) erinnert mich stark an die Assemblersprache der IBM/360-Computer und deren Nachfolger. Auch dort gab es (gegen Aufpreis, versteht sich) ein Makropaket für die strukturierte Programmierung (If-Else, verschiedene Schleifenkonstrukte, Switch-Case usw.). Mit diesem Assembler wurde nicht nur hardwarenah (Treiber u.ä.) programmiert, er wurde tatsächlich wie eine Hochsprache für kaufmännische Software (als Alternative zu Cobol) eingesetzt. Mittlerweile sind aber viele Jahrzehnte vergangen, und die Software- entwicklungsmethoden haben sich völlig verändert. Eingefleischte IBMler nutzen aber immer noch ihren Assembler und verwenden – ähnlich wie auch du – sogar ihren eigenen Zeichensatz (EBCDIC). Erst vor kurzem hatten wir eine amüsante Diskussion, in der dieses Thema aufkam: Beitrag "Was hat es mit den 'trigraphs' aufsich" Kann es vielleicht sein, dass du bei IBM beschäftigt bist oder warst?
Es gibt in der Tat eine Eins-zu-Eins-Entsprechung zwischen dem Quelltext und dem erzeugten Code. Und dies ermöglicht es, durch Addieren von Ausführungszeiten die Dauer von Programmteilen zu berechnen. Ob es dafür einmal eine ernsthafte Anwendung geben wird, wird man sehen.
Josef G. schrieb: > Ob es dafür einmal eine > ernsthafte Anwendung geben wird, wird man sehen. Da sich offensichtlich niemand damit beschäftigen will, wird sich die Verbereitung und damit die Anwendung in Grenzen halten. Vermutlich sind die Grenzen gleichzeitig durch deine eigenen 4 Wände definiert... Oo
Manuel X. schrieb: > Vermutlich sind > die Grenzen gleichzeitig durch deine eigenen 4 Wände definiert... Was ja nicht eine ernsthafte Anwendung ausschließt....
Yalu X. schrieb: > Mittlerweile sind aber viele Jahrzehnte vergangen, und die > Software-entwicklungsmethoden haben sich völlig verändert. > Kann es vielleicht sein, Richtig ist, dass ich schon etwas älter bin und mir vieles an den modernen Entwicklungen nicht gefällt.
Josef G. schrieb: > Richtig ist, dass ich schon etwas älter bin und mir > vieles an den modernen Entwicklungen nicht gefällt. Ich bin auch nicht mehr der Jüngste, und auch mir gefällt vieles an den modernen Entwicklungen nicht. Aber wie alt musst du sein, dass du die Infixschreibweise arithmetischer Ausdrücke (wie bspw. y = m * x + b) in einer höheren Programmiersprache als "moderne Entwicklung" empfindest? Diese Entwicklung hat immerhin schon vor deutlich über einem halben Jahrhundert stattgefunden ;-)
Josef G. schrieb: > Siehe meine Benutzerseite, da ist meine Website zu finden. Die Sprache > wird beschrieben auf der Seite SYS-doku. Diese Chaosseiten sind eine Strafe - unklar ist mir eigentlich nur, was man ausgefressen haben muss, um dazu verdonnert zu werden, diesen Krempel zu lesen. Josef, du tätest wirklich gut daran, dir endlich die Grundlagen der Informatik anzueignen, statt immer wieder Anfänge aus den 1960er Jahren "neu" zu "erfinden". Wenn du dir unbedingt mit Reto-Technologien einen Namen machen willst, musst du eine nehmen, was wenigstens ein paar Tausend Jahre alt ist und heute nicht mehr gebräuchlich ist, z.B. das Herstellen von Steinzeitwerkzeugen und -waffen.
:
Bearbeitet durch User
Yalu X. schrieb: > Ausdrücke (wie bspw. y = m * x + b) in einer höheren Programmiersprache Ausdrücke lehne ich keineswegs ab. Ich habe selber geschrieben Josef G. schrieb: > Ein Schwachpunkt der Sprache: Sie kennt keine Ausdrücke. Bei der von mir gewählten Methode, anstelle von Ausdrücken Folgen von "Mikrooperationen" zu verwenden, hat der Programmierer die volle Kontrolle über den tatsächlich ausgeführten Code, und die Dauer lässt sich einfach berechnen, wenn die Dauer der einzelnen Mikrooperationen bekannt ist. Andererseits werden diese Folgen recht schnell sehr umfangreich, insbesondere bei Verwendung von Arrays. Denkbar wäre, dass es irgendwann so etwas wie einen Präprozessor gibt, der Ausdrücke im Quelltext in Folgen von Mikrooperationen umwandelt.
Josef G. schrieb: > Bei der von mir gewählten Methode, anstelle von Ausdrücken Folgen > von "Mikrooperationen" zu verwenden, hat der Programmierer die volle > Kontrolle über den tatsächlich ausgeführten Code, "Real programmers write Microcode. Assembler is a high level language."
Josef G. schrieb: > Denkbar wäre, dass es irgendwann so etwas wie einen Präprozessor gibt, > der Ausdrücke im Quelltext in Folgen von Mikrooperationen umwandelt. Manche Leute nennen das einen "Compiler". ;-)
:
Bearbeitet durch User
A. K. schrieb: > Manche Leute nennen das einen "Compiler". ;-) ... und das, was Josef einen Compiler nennt, einen Makro-Assembler ;-)
A. K. schrieb: > Josef G. schrieb: >> Denkbar wäre, dass es irgendwann so etwas wie einen Präprozessor gibt, >> der Ausdrücke im Quelltext in Folgen von Mikrooperationen umwandelt. > > Manche Leute nennen das einen "Compiler". ;-) Nein. Der Präprozessor würde Text in Text umwandeln. Meine Aussage war hier missverständlich, da ich Mikrooperationen geschrieben habe, aber die Text-Kürzel gemeint waren, welche für die Mikrooperationen stehen und vom Compiler durch die Mikrooperationen ersetzt werden. Josef G. schrieb: > Der Compiler erzeugt keinen Maschinencode, sondern einen Code, > welcher interpretativ von der CPU abgearbeitet wird. Die "Mikrooperationen" sind keine CPU-Operationen, sondern Elemente des vom Compiler erzeugten (Zwischen-)Codes. Die Bezeichnung Compiler ist zB. auch bei der Sprache Java üblich, wo es ebenfalls einen solchen Zwischencode gibt.
Falls jemand sich grundsätzlich für das System interessiert, aber die Dokumentation für unverständlich hält: Vielleicht würde es helfen, erst einmal mittels des Emulations- Programms sich die Quelltexte der Beispielprogramme anzuschauen und die Programme zu testen. Insbesondere das erste (emtext_a) würde ich empfehlen.
Josef G. schrieb: > Insbesondere das erste (emtext_a) > würde ich empfehlen. Also ich kriege da nur eine Datei mit lauter Zahlen, nischt mit 'Quelltext'. Für mich sieht das ganze wieder mal nach 'Habe Gerät, suche Anwendung' aus. Da ist Assembler auf dem 8051 richtig entspannend gegen das, was ich da auf deiner Webseite gelesen habe. Wer soll sich denn da durchfinden? Dann doch lieber richtige Herausforderungen wie 'CP/M auf dem 8048' oder 'Gleitkommaberechnung mit Diodenmatrix' :-P Richtig interessant finde ich auch sowas hier: http://www.vaxman.de/my_machines/hitachi/240/240.html
Matthias Sch. schrieb: > Also ich kriege da nur eine Datei mit lauter Zahlen, Man braucht das Emulationsprogramm, um den Text anzuschauen. > Für mich sieht das ganze wieder mal nach 'Habe Gerät, > suche Anwendung' aus. Ist richtig, hab ich nie bestritten.
Josef G. schrieb: > Nein. Der Präprozessor würde Text in Text umwandeln. Es gab C++ - Compiler, die C++ - Code in C übersetzten. Transpiler übersetzen Code einer Sprach in welchen einer anderen. Ich würde sagen, dass das gemeinsame Kriterium von Compilern der Aufbau eines Syntax-Baumes ist, aus dem anschließend das Endprodukt erzeugt wird. Ob das Text, Binärcode, oder sonstwas ist, ist dabei egal. https://de.wikipedia.org/wiki/Compiler#Einordnung_verschiedener_Compiler-Arten
Josef G. schrieb: > Vielleicht würde es helfen, erst einmal mittels des Emulations- > Programms sich die Quelltexte der Beispielprogramme anzuschauen > und die Programme zu testen. Insbesondere das erste (emtext_a) > würde ich empfehlen. Dazu müsstest du erst mal deine elende "Dokumentation" in ein halbwegs verdauliches und übersichtliches Format bringen. So wie sie im Moment ist, ist sie nur für Masochisten und Buchhalter geeignet.
Vielleicht könnte ja mal jemand, der meint, die Dokumentation sei unverständlich, mir eine konkrete Stelle nennen, wo etwas unverständlich ist.
Josef G. schrieb: > Vielleicht könnte ja mal jemand, der meint, die Dokumentation > sei unverständlich, mir eine konkrete Stelle nennen, wo etwas > unverständlich ist. Im Allgemeinen: Zuerst einmal wirkt deine Dokumentation so, als ob du während deiner Entwicklung, die für dich wesentlichen Teile in reine Textdateien geschrieben hast, und diese nun 1:1 auf deinen Server hochgeladen hast. Die gesamte Dokumentation erfüllt sicherlich den Zweck für dich als persönliche Referenz, ist aber völlig ungeeignet um neue Menschen für dein System zu begeistern. Also erschlage den interessierten Leser nicht gleich mit Fakten, sondern nimm ihn bei der Hand, und zeige Schritt für Schritt, was er mit dem System anstellen kann. In der jetzigen Form, muss man die komplette Seite mehrfach lesen um irgendwie einen Einstieg zu finden. Konkret: Als interessierter Leser klicke ich erstmal auf "SYS-doku", weil ich dahinter die Systemdokumentation vermute. Der erste Abschnitt lautet: > Die Zuordnung der Adressregister zu einer memory-page erfolgt > durch 2bit-Latches MP,MX,MY,MZ , und es gibt Austauschregister > NP,NX,NY,NZ . Das CPU-Signal B# tauscht M#-N# (#= P,X,Y,Z). Interessiert mich das? Nein! Was spräche dagegen, z.B. deinen Emulator zu zeigen, wie man diesen bedient, und ein einfaches Programm zum laufen kriegt? Schau dir mal Programmierlehrbücher an, und wie diese aufgebaut sind. Wo finde ich eine gegliederte Darstellung? Das ganze ist ein einziger Fließtext ohne Struktur. Zum Webdesign: Es spricht nichts gegen puristisches Webdesign. Aber Links und Bilder gibt es schon ne Weile, und diese kann und sollte man benutzen.
Josef G. schrieb: > Matthias Sch. schrieb: >> Also ich kriege da nur eine Datei mit lauter Zahlen, > > Man braucht das Emulationsprogramm, um den Text anzuschauen. Das z.B. ist etwas, das Interessierte schon mal abschreckt - was für ein Emulationsprogramm, und wieso kann man einen Quelltext nicht, wie bei jedem anderen System seit Urzeiten, im Klartext lesen? Die Token (oder was auch immer da drin steht) interessieren mich am Anfang doch gar nicht und 'Quelltext' ist für mich eben genau das - Text, der als Quelle für einen wie auch immer gearteten Compiler, Assembler, Tokenizer oder Interpreter dient, der dann irgendwann Maschinencode erzeugt. Und wenn ich mir einen Eindruck von einem Rechnersystem verschaffen will, dann schau ich mir gerne auch mal den Assemblertext (oder wie auch immer das bei dir heisst) an. Dumm eben auch, wenn man vor einem Rechner mit z.B. Windows oder Mac OS sitzt, dann kriegt man den Emulationator nicht mal ohne Verrenkungen zum Laufen. Du könntest also ganz einfach mal eines der Programme umschnurzeln und als Klartext auf die Webseite setzen. Nächster Punkt: Du redest zwar von irgendwelche Steckkarten und Programmen dadrauf, aber mir ist der Sinn des Ganzen nicht klargeworden. Sind das Option ROMs wie bei einem Apple ][ oder IBM PC oder was ist der Sinn dahinter? Du hast dich in Flipflops,Register und Signalbezeichnungen verstrickt, aber vergessen, einem Typen wie mir zu erklären, warum und wofür du das überhaupt machst. Das war z.B. einer der grossen Vorzüge beim IBM PC oder dem o.a. Apple ][. Ein offenes System, das jeder nachvollziehen konnte, mit Unterlagen, und einem einfach zu verstehenden System aus Erweiterungen und der Möglichkeit, mal eben selber eine Karte zu stricken und die durch den Rechner anzusprechen. Gut, in den Zeiten von PCI-Express und USB ist das ein wenig schwieriger geworden, aber immer noch vollständig dokumentiert und nachvollziehbar.
:
Bearbeitet durch User
Matthias Sch. schrieb: > Nächster Punkt: Du redest zwar von irgendwelche Steckkarten und > Programmen dadrauf, aber mir ist der Sinn des Ganzen nicht klargeworden. > Sind das Option ROMs wie bei einem Apple ][ oder IBM PC oder was ist der > Sinn dahinter? > > Das war z.B. einer der grossen Vorzüge beim IBM PC oder dem o.a. Apple > ][. Ein offenes System, das jeder nachvollziehen konnte, mit Unterlagen, > und einem einfach zu verstehenden System aus Erweiterungen und der > Möglichkeit, mal eben selber eine Karte zu stricken und die durch den > Rechner anzusprechen. Genauso ist auch mein Rechner konzipiert. Beim AppleII belegte jeder Steckplatz 256 Byte (oder waren es 512). Die Adressbereiche waren übereinander angeordnet. Hat man bei einer Karte den Steckplatz gewechselt, haben sich die Adressen geändert. Mir hat das nicht gefallen. Und die Kommandos an die Karte wurden mittels PRINT zeichenweise an die Karte gesendet. Bei meinem System hat jede Karte volle 64KByte zur Verfügung. Die Namen der Karten-Kommandos werden bereits beim Compilieren durch Nummern ersetzt. Es gibt sauber definierte Schnittstellen für den Aufruf der Kommandos und des "Formelinterpreters", so habe ich das auf der Karte befindliche Programm genannt, welches die oben genannten Folgen von "Mikrooperationen" ausführt. Auf die Karten wird über die Steckplatz-Nummer zugegriffen, das erfolgt indirekt über eine Ersetzungstabelle. Wenn man eine Karte in einen anderen Steckplatz steckt, muss man nur diese Tabelle ändern.
@ Josef G. (bome) Benutzerseite >> Das war z.B. einer der grossen Vorzüge beim IBM PC oder dem o.a. Apple >> ][. Ein offenes System, das jeder nachvollziehen konnte, mit Unterlagen, >> und einem einfach zu verstehenden System aus Erweiterungen und der >> Möglichkeit, mal eben selber eine Karte zu stricken und die durch den >> Rechner anzusprechen. >Genauso ist auch mein Rechner konzipiert. Meinst DU! Nur mit dem "kleinen" Unterschied, dass Apple II und Ur-PC locker 35 Jahre her sind, damals ein Novum waren, innerhalb kürzester Zeit Massen von Leuten begeisterten und die Erbauer zu reichen Leuten gemacht haben. Aber was rede ich hier über Nebensächlichkeiten. Facepalm
ich find es ja (wirklich!) beeindruckend, wenn jemand eine CPU (ganzen Rechner?) selber entwickelt.. (ich könnte das nicht) ist eben so, als baute man eine Balliste oder ein Katapult (das machen auch noch viele Leute aus Spaß/Nostalgie, und nicht weil man heute noch einen nutzen davon hätte..) zum Thema (schleife mit Hirneinspringen) bin ich grundsätzlich der Meinung, dass du dich hier in irgend einem (total unwichtigen) Detail verrennst.. ob man das hat, oder nicht ist doch egal.. mich würde immer noch interessieren, wie solch eine Schleife verschachtelt ausschaut (und zwar in realem code, nicht skizziert...) kannst da mal an Screenshot machen? Falls jemand wirklich glaube, der Typ von schleife wäre sinnvoll, könnte man seine Zeit doch auch damit verbringen das bei z.b. FreePascal oder GCC (in einem Fork) einzubauen.. und herzeigen wie toll man damit tägliche Probleme lösen kann.. das mit den Steckkarten errinnert mich an den GameBoy/C64 usw. warum es wichtig sein sollte, dass man (anhand vom Code) abzählen kann, wieviele takte ein bestimmtes programm jetzt genau braucht, kapier ich übrigens nicht.. ps. auch interessant ist, dass die Homepage die ausschaut als wäre sie mit der Scheinmaschine geschrieben wurde, mit iWeb erstellt wurde ...
1 | *PGM DEMO |
2 | *CON STRG -?? |
3 | *AUT AA. /00 BB. /00 |
4 | *BEGN |
5 | *L.JP |
6 | 1LFEED |
7 | *L.JP |
8 | 1ADDLOC /01 |
9 | =DECR AA. |
10 | *HERE |
11 | 1PRINTN /08 AA. |
12 | *RP.S AA. |
13 | 1LFEED |
14 | *HERE |
15 | 1SETLOC /f0 |
16 | *L.JP |
17 | 1PRINTS /02 STRG |
18 | *HERE |
19 | 1PRINTS /00 STRG |
20 | 1INPUTN /08 AA. |
21 | 1INPUTN /0c BB. |
22 | 1LFEED |
23 | *RP.Z BB. |
24 | 1 oGET AA. oAND /f8 oSTO BB. |
25 | *RP.Z BB. |
26 | *RETN |
Robert L. schrieb: > mich würde immer noch interessieren, wie solch eine Schleife > verschachtelt ausschaut (und zwar in realem code, nicht skizziert...) > kannst da mal an Screenshot machen? Habe also ein Beispiel erstellt, wo sowohl zwischen L.JP und HERE als auch zwischen HERE und RP.x eine weitere Schleife mit Hinein- sprung steht. Siehe part1.png und part2.png, darunter nochmal im Ganzen. run.png zeigt den Ablauf beim Compilieren und Testen. Das Programm liest zwei Zahlen ein, wobei davor ?? ausgegeben wird. Die Eingabe wird wiederholt, falls die zweite Zahl Null ist. Bei den Wiederholungen wird zusätzlich ein zweites ?? ausgegeben. Wenn anschließend die erste Zahl größer ist als 7, erfolgt Abbruch, andernfalls wird diese Zahl ausgegeben und in derselben Zeile bis Null dekrementiert und es geht wieder zur Eingabe. Das Programm dient ausschließlich dazu, die Verschachtelbarkeit der Schleife mit Hineinsprung zu demonstrieren. Niemand würde sonst ein Programm so schreiben.
:
Bearbeitet durch User
Eine Hochsprache ist das ja nicht gerade... Wer programmiert heutzutage noch ernsthaft Assembler? Höchstens Bastler und Nostalgiker. Josef G. schrieb: > In meiner Sprache gibt es kein GOTO, und die Blockstruktur > ist streng und kann nicht durchbrochen werden. Wozu man einem Assembler eine "strenge Blockstruktur" aufbrummen muss, erschließt sich mir nicht. Warum man die "strenge Blockstruktur" anschließend wieder mit so einer komischen Konstruktion, wie dem "Hineinsprung" durchlöchern muss, schon zweimal nicht. Das ganze ist doch nur reine Beschäftigungstherapie. Ich kenne aus den 1970ern noch ein Sprachkonstrukt, das ich seither nicht wieder gesehen habe und das ich wirklich interessant finde: Das Betriebssystem MCP der Borroughs 1700er-Serie wurde in einer Sprache namens SDL (System Development Language) programmiert. Das war eine echte Hochsprache (ohne Fließkomma-Arithmetik!) mit folgendem Schleifenkonstrukt:
1 | do outerloop forever /* unendliche Schleife! */ |
2 | ... |
3 | ... |
4 | do innerloop forever |
5 | ... |
6 | ... |
7 | undo outerloop |
8 | ... |
9 | end |
10 | ... |
11 | ... |
12 | end |
- Die Schleifennamen (hier: outerloop, innerloop) waren optional und frei wählbar - undo entspricht dem break in C - undo ohne Label beendet die Schleife, in der undo steht - undo mit Label beendet die gelabelte Schleife SDL hatte kein goto und mit dem undo hatte man einen Sprung, der keine "Sauereien" zuließ und die Schleifenrücksprünge aus geschachtelten Schleifen sehr bequem und sicher machte.
:
Bearbeitet durch User
Uhu Uhuhu schrieb: > Warum man die "strenge Blockstruktur" anschließend wieder mit so einer > komischen Konstruktion, wie dem "Hineinsprung" durchlöchern muss, Die wird nicht durchlöchert. L.JP .. HERE .. RP.x ist genauso ein Block mit zwei Unterblöcken, wie dies bei if .. else .. end-else der Fall ist.
Du hast das Zitat um einen wichtigen Teil verstümmelt und den eigentlichen Punkt umgangen: Assembler mit "strenger Blockstruktur" sind Unsinn.
Uhu Uhuhu schrieb: > Wozu man einem Assembler eine "strenge Blockstruktur" aufbrummen muss, > erschließt sich mir nicht. Wenn man schon in Assembler programmiert, dann kann das sehr wohl Sinn ergeben. Ganz besonders dann, wenn man nichts anderes zur Verfügung hat oder Hochsprachen generell ablehnt (haben wir hier ja so einen oder zwei im Forum). > Warum man die "strenge Blockstruktur" anschließend wieder mit so einer > komischen Konstruktion, wie dem "Hineinsprung" durchlöchern muss, schon > zweimal nicht. Ist doch langweilig, immer bloss den gleichen Strukturen von anno Algol nachzueifern.
In meiner Sprache gibt es den Datentyp "Databox". Es gibt den Mechanismus des "Ausleihens", wobei Variable, welche zuvor keinen Wert haben (reine Zeiger sind), innerhalb der Databox ihren Wert zugewiesen bekommen. Die Werte müssen danach wieder "zurückgegeben" werden, bevor die Databox nicht mehr existiert, andernfalls würden die Werte in undefinierten Bereichen des RAM liegen und Schreib- zugriffe könnten Unheil anrichten. (Das hat Ähnlichkeit mit üblichen Konstruktionen wie "with record do", wo die Elemente des records innerhalb dieses Blocks verfügbar sind.) Der Compiler garantiert, dass das Zurückgeben innerhalb desselben Blocks erfolgt wie das Ausleihen. Genau zu diesem Zweck wird die Blockstruktur benötigt, und da darf es auch kein Herausspringen aus Schleifen geben.
Uhu Uhuhu schrieb: > Ich kenne aus den 1970ern noch ein Sprachkonstrukt, das ich seither > nicht wieder gesehen habe und das ich wirklich interessant finde: PL/I:
1 | A: DO I = 1 TO 10; |
2 | DO J = 1 TO 5; |
3 | IF X(I,J)=0 THEN |
4 | LEAVE A; |
5 | END; |
6 | END; |
oder kürzer:
1 | A: DO I = 1 TO 10; |
2 | DO J = 1 TO 5; |
3 | IF X(I,J)=0 THEN |
4 | LEAVE A; |
5 | END A; |
:
Bearbeitet durch User
Uhu Uhuhu schrieb: > A. K. schrieb: >> PL/I: > > Das war etwa dieselbe Zeit... Beispiele aus jüngerer Zeit: In Java gibt es ähnlich wie in PL/I labeled Loops:
1 | outerloop:
|
2 | for(x=0; x<10; x++) { |
3 | for(y=0; y<10; y++) { |
4 | if(condition(x, y)) |
5 | break outerloop; |
6 | }
|
7 | }
|
Ähnlich geht es auch in Javascript, Ada, Perl, Fortran 90 (und neuer), Go, Swift und sicher noch etlichen weiteren Sprachen. In PHP gibt es
1 | break n; |
wobei n angibt, wieviele der verschachtelten Schleifen verlassen werden sollen. Dabei entspricht break 1 einem break ohne Argument. In Python und Rust wurde so ein Konstrukt vorgeschlagen, dann aber abgewiesen. Die Hineinspringschleife von Josef ist im Vergleich dazu schon etwas Besonderes :)
:
Bearbeitet durch Moderator
Yalu X. schrieb: > In Java gibt es ähnlich wie in PL/I labeled Loops: Diese "Wenn Prädikat dann Abbrechen-Befehl"-Konstruktionen kann man direkter so schreiben:
1 | found = false; |
2 | for(x=0; x<10 && !found; x++) |
3 | for(y=0; y<10 && !found; y++) |
4 | found = test(x, y); |
5 | |
6 | if (found) |
7 | use(x, y); |
Man sieht sofort in den Schleifenbedingungen dass sie frühzeitig (bei erfolgreicher Suche) abgebrochen werden und man brauch das "found" in der Regel sowieso später, da man wissen möchte ob die Suche erfolgreich war oder nicht. Die Alternative die Schleifenvariablen 'x' und 'y' nach der Suche erneut auf gültige Werte zu testen, ist viel zu fehleranfällig. Das "use(x, y)" in die Schleife packen und und danach sofort abbrechen, ist noch viel unübersichtlicher, da die beiden Schritte "erst mal suchen" und "wenn gefunden dann benutzen", ineinander verschachtelt werden und somit der Programmfluss schlecht ersichtlich ist.
@ Unbekannt Unbekannt (unbekannter) >Diese "Wenn Prädikat dann Abbrechen-Befehl"-Konstruktionen kann man >direkter so schreiben: Endlich mal jemand mit Durchblick und klarer Ansage! Diese Diskussion ist sowas von hirnrissig! Mental gesunde Menschem müssen und sollten sich NICHT mit diese Josefschen Konstrukt beschäftigen, es sein denn, ihnen ist an der Nichtaufrechterhaltung ihrer mentalen Gesundheit gelegen.
Unbekannt Unbekannt schrieb: > Man sieht sofort in den Schleifenbedingungen dass sie frühzeitig (bei > erfolgreicher Suche) abgebrochen werden und man brauch das "found" in > der Regel sowieso später, da man wissen möchte ob die Suche erfolgreich > war oder nicht. Deshalb ja auch der geliebte wie verhasste C Klassiker:
1 | for(x=0; x<10; x++) |
2 | for(y=0; y<10; y++) |
3 | if(test(x,y)) |
4 | goto found; |
5 | fail(); |
6 | found: |
7 | run(x,y); |
:
Bearbeitet durch User
Noch ein kleines Beispiel aus dem Kuriositätenkabinett: In Fortran sind Unterprogramme mit mehreren Einstiegspunkten möglich. Das folgende Beispiel zeigt eine Zählfunktion, die entweder aufgerufen als icounter() dann wird der aktuelle Inhalt der internen Zählvariable icnt zurückgegeben und diese inkrementiert, oder als icounterinit(istart) dann wird zusätzlich icnt auf einen neuen Startwert gesetzt:
1 | program test |
2 | print *, icounter() |
3 | print *, icounter() |
4 | print *, icounterinit(100) |
5 | print *, icounter() |
6 | print *, icounter() |
7 | end |
8 | |
9 | function icounterinit(istart) |
10 | data icnt /0/ |
11 | icnt = istart |
12 | entry icounter() |
13 | icounter = icnt |
14 | icnt = icnt + 1 |
15 | end |
Ausgabe:
1 | 0 |
2 | 1 |
3 | 100 |
4 | 101 |
5 | 102 |
Mit diesem Konstrukt kann man bspw. einen Zufallszahlengenerator ohne die Verwendung einer globalen Statusvariable realisieren.
Offenbar mit Ähnlichem im Sinn war in C anfangs das Schlüsselwort "entry" reserviert.
:
Bearbeitet durch User
Yalu X. schrieb: > Ähnlich geht es auch in Javascript, Ada, Perl, Fortran 90 (und neuer), > Go, Swift und sicher noch etlichen weiteren Sprachen. Aha, dann bin ich also durch jahrzehntelanges C-Programmieren verdorben... Ist halt doch eigentlich eine Scheiß-Sprache :-( > In PHP gibt es > break n; > > wobei n angibt, wieviele der verschachtelten Schleifen verlassen werden > sollen. Dabei entspricht break 1 einem break ohne Argument. Dass die PHP-Fritzen einen Trick finden werden, der die gute Idee wieder in einen Mist umwandelt, den man lieber nicht will, war zu erwarten... Yalu X. schrieb: > In Fortran sind Unterprogramme mit mehreren Einstiegspunkten möglich. Soll das jetzt etwa eine Anregung für Josef sein?
Auch wenns OT ist, in C# kann man so etwas ähnliches mittels yield machen.
1 | using System; |
2 | using System.Collections.Generic; |
3 | using System.Linq; |
4 | using System.Text; |
5 | using System.Threading.Tasks; |
6 | |
7 | namespace ConsoleApplication1 |
8 | { |
9 | class Program |
10 | { |
11 | static void Main(string[] args) |
12 | { |
13 | foreach(int value in count(10)) |
14 | { |
15 | Console.Write(value + " "); |
16 | } |
17 | |
18 | Console.ReadKey(); |
19 | } |
20 | |
21 | static IEnumerable<int> count(int count) |
22 | { |
23 | for (int i = 0; i < count; i++) |
24 | { |
25 | yield return i; |
26 | } |
27 | } |
28 | } |
29 | } |
Ausgabe:
1 | 0 1 2 3 4 5 6 7 8 9 |
Marten W. schrieb: > Auch wenns OT ist, in C# kann man so etwas ähnliches mittels yield > machen. Auch wieder nur eine halbherzige Flickschusterei. Es ist erstaunlich, wie "schwach" viele der verbreiteten Programmiersprachen entworfen wurden. Teils historisch bedingt, teils weil man auf allen Hochzeiten tanzen möchte. Sei's drum. Das "yield"-Konzept ist den mehr als 50 Jahren alten "Koroutinen" entliehen: http://en.wikipedia.org/wiki/Coroutine
Josef G. schrieb: >
1 | > *PGM DEMO |
2 | > *CON STRG -?? |
3 | > *AUT AA. /00 BB. /00 |
4 | > *BEGN |
5 | > *L.JP |
6 | > 1LFEED |
7 | > *L.JP |
8 | > 1ADDLOC /01 |
9 | > =DECR AA. |
10 | > *HERE |
11 | > 1PRINTN /08 AA. |
12 | > *RP.S AA. |
13 | > 1LFEED |
14 | > *HERE |
15 | > 1SETLOC /f0 |
16 | > *L.JP |
17 | > 1PRINTS /02 STRG |
18 | > *HERE |
19 | > 1PRINTS /00 STRG |
20 | > 1INPUTN /08 AA. |
21 | > 1INPUTN /0c BB. |
22 | > 1LFEED |
23 | > *RP.Z BB. |
24 | > 1 oGET AA. oAND /f8 oSTO BB. |
25 | > *RP.Z BB. |
26 | > *RETN |
27 | > |
Gratulation. Ich dachte ich hätte schon vieles gesehen aber so was hab ich auch noch nie gesehen. Mit Ausnahme vielleicht von meiner ersten Begegnung mit APL ist es mir seit Jahrzehnten nicht mehr passiert, dass ich in angeblichem Quelltext nicht das Geringste verstehe. Noch nicht mal ansatzweise Da sag noch mal einer, C wäre kryptisch.
Erinnert entfernt an manchen frühen Assemblercode, oder irgendwelche Drucker- oder Job-Controls. Frühe Assembler waren mitunter ähnlich kryptisch: http://en.wikipedia.org/wiki/COMPASS/Sample_Code
:
Bearbeitet durch User
Karl Heinz schrieb: > Gratulation. > > Ich dachte ich hätte schon vieles gesehen aber so was hab ich auch noch > nie gesehen. Mit Ausnahme vielleicht von meiner ersten Begegnung mit APL > ist es mir seit Jahrzehnten nicht mehr passiert, dass ich in angeblichem > Quelltext nicht das Geringste verstehe. Noch nicht mal ansatzweise > Da sag noch mal einer, C wäre kryptisch. Das ist doch ganz einfach. Josefs Code ist absolut intuitiv und auch für den Nicht-Eingeweihten komplett lesbar! Ich erklärs Dir: > *PGM DEMO Wir beginnen ein Programm namens "DEMO". Beachte die Großbuchstaben. Kleinbuchstaben sind nicht erlaubt. Warum? Josef benutzt immer noch einen 5-Bit-Lochkartenstanzer. > *CON STRG -?? Einer der einfachsten Befehle: Schreibe auf die Console. Das "-??" ist ein geheimer Code für die Ausgabe von: HALLO KARL HEINZ, HEUTE IST MITTWOCH, DER 29. OKTOBER, WIR HABEN 12:37 UND EINE ZIMMERTEMPERATUR VON 22 GRAD. MORGEN WIRD ES REGNEN UND KALT. BITTE GIB NUN ZWEI ZAHLEN EIN: Wohlgemerkt: Nur Großbuchstaben. Daran müsste Josef nochmal arbeiten. > *AUT AA. /00 BB. /00 AUT steht für AUTOMATISCHE EINGABE VON ZAHLEN. Hier nennt Josef diese AA. und BB, beide vorbelegt mit 00. Das "/" vor den Zahlen 00 soll nur den geneigten Leser verwirren: Es handelt sich NICHT um eine Division durch Null! Merken! > *BEGN Wir beginnen endlich - nach der Eingabe von 2 Zahlen - mit der Abarbeitung des Programms. Das Kürzel BEGN statt BEGINN ist besonders pfiffig. Josef spart damit Vokale, macht das Programm kompakter und für NSA-Mitarbeiter schwieriger. Das ist ABSICHT! > *L.JP Oh, jetzt kommts richtig heftig: Das L steht für LOOP, das wissen nur die wirklich eingeweihten! Aber macht nichts, ich habe dies nach nächtelangem Einsatz von 1024-Bit-Key-Entschlüsselungsroutinen auf einer alten Cray im Münchener Museum ganz klar herausgefunden! Und JP steht selbstverständlich JAM POT, achnee JUMP. Wir springen also jetzt mit Lichtgeschwindigkeit in eine Schleife..... festhaaaaaaalten! > 1LFEED Eine der einfachsten Anweisungen überhaupt. Wörtlich übersetzt heisst das Mach ein Line Feed Warum Josef hier FEED komplett ausschreibt, ist mir absolut rätselhaft. Er hätte es auch mit Q abkürzen können, um der Programmiersprache noch etwas mehr Transparenz zu geben. Denn 1 LQ wäre viel viel verständlicher gewesen, oder nicht? Wenn man nun noch die Transponierung von L nach I vornimmt, steht da 1 IQ welches klar die Intelligenz dieses phänomenalen Programms ausdrücken soll. Aber wir schweifen ab... zurück zum Code: > *L.JP Tja, kennen wir schon: Wir springen in die innere Schleife. Weitere Kommentare sind hier überflüssig. > 1ADDLOC /01 DAAAAAS ist der absolut Knüller! Wir addieren auf die erste Variable (wegen der 1 vor ADDLOC, gemeint ist also AA) eine 1. > =DECR AA. Und hier dekrementieren wir wieder AA. Und jetzt das verblüffendste: Der Wert von AA ist danach wieder ABSOLUT IDENTISCH mit der eingegebenen Zahl!!!!!11111 > *HERE Das HERE steht selbstverständlich nicht für HERE, also "hier", sondern für "da", also THERE! Hier müsste der Compiler eigentlich mit einem Syntax-Error aussteigen. Aber Josef hat vorgesorgt. Bei einem Syntax-Error googelt der Compiler automatisch nach der Bedeutung des Wortes, um dann den Sinn einfach umzudrehen! Das ist GENIAL!!!! So werden Programmierfehler automatisch korrigiert! > 1PRINTN /08 AA. Tja, PRINTN ist wirklich ziemlich geschwätzig. PT hätte mir ja besser gefallen. Aber was passiert hier? Ganz einfach: Die Zahl AA wird gedruckt - und zwas auf die Console. Und Hurra! Das Programm gibt exakt die Zahl wieder aus, die der User vorher eingegeben hat! Das ist Wahnsinn! Ein Programm, welches die Gedanken des Anwenders lesen (und sicher auch verstehen!) kann! Die /08 sind klar? Nein? Ganz einfach: "Formatiere die Ausgabe rechtsbündig auf 8 Stellen.". Wenn Josef linksbündig meint, dann schreibt er "08/", wenn er zentrieren will, dann "/08/". Echt pfiffig. > *RP.S AA. Okay, das ist auf den ersten Blick ziemlich verwirrend hier, aber ist für den Experten sofort klar: Das ist ein einfacher NOP. Kennt ja jeder. Das genialische: Man kann bei Josefs Rechner dem NOP ein Argument mitgeben! Jawoll! Es sollen hier also soviele NOPs gemacht werden, wie AA als Zahl bedeutet. > 1LFEED Mittlerweile ist die Console nicht mehr busy und kann endlich eine neue Zeile ausgeben. Ist sinnig, sonst steht der Druckerkopf von Josefs Teletype-Terminal noch hinter der ausgegebenen Zahl und versperrt den Blick auf diese. > *HERE Nochmal: DA! Guck DA! Also THERE! > 1SETLOC /f0 Oh, jetzt wird Josefs Programm prozessor-spezifisch. Das heißt soviel wie: LOKALISIERE DEN VERDAMMTEN F0-BUG AUF DIESER CPU! Damit beugt Josef einem Fehler von INTEL vor, welcher das Programm ohne diese Vorsichtsmaßnahme zum Crash bringen könnte. Aber noch ist Josefs Rechner nicht auf INTEL-Rechner portiert. GUT SO! > *L.JP Springe in Schleife... >> 1PRINTS /02 STRG Jetzt wird nochmal der Begrüßunssting gedruckt, also eigentlich den von oben: HALLO KARL HEINZ, HEUTE IST MITTWOCH, DER 29. OKTOBER, WIR HABEN 12:37 UND EINE ZIMMERTEMPERATUR VON 22 GRAD. MORGEN WIRD ES REGNEN UND KALT. Durch die Formatierung /02 wird daraus aber: HA Hier hat Josef sich geirrt. Es muss sich um einen Tippfehler handeln, denn es hätte /03 heißen müssen. Nur dann würde diese Message auch einen Sinn ergeben: HAL Ja, es handelt sich um HAL aus "Odyssee 2001"! Das Rätsel ist gelüftet! Josef wars! > *HERE > 1PRINTS /00 STRG > 1INPUTN /08 AA. > 1INPUTN /0c BB. > 1LFEED > *RP.Z BB. Diese Befehle habe ich oben schon erklärt, daher überspringe ich das. > 1 oGET AA. oAND /f8 oSTO BB. Das ist wieder ganz intuitiv. frei übersetzt: SCHNAPPE DIR AA UND SPEICHERE DAS IN BB Ja. Jetzt hat der Rechner die Gedanken des Users von AA nach BB kopiert und für die Ewigkeit gespeichert... > *RP.Z BB. Kennen wir auch schon: Das war der NOP. Bevor wir also gleich das Programm beeenden, ärgern wir den Rechner nochmal mit unsinnigen Befehlen. Und das gleich BB-mal! > *RETN Kennt jeder: RETURN. Fragt sich nur wohin? Aber das werden wir dann in der nächsten Stunde erklären.... brzzzz krrrrchs....
Karl Heinz schrieb: > Josef G. schrieb: >>> *PGM DEMO > jede Menge Zeugs entfernt >> *RETN >> > Gratulation. Auch von mir beste Wünsche. Ich habe selten so etwas fehlerträchtiges und unlesbares gesehen. Trotz Franks ausgezeichneter Erklärung der einzelnen Programmteile bin ich jetzt froh, mir keinerlei Emulazionator für dieses ulkige Kauderwelsch geladen/kompiliert/eingetreten zu haben. Vielen Dank, Frank, das du so viel deiner Zeit geopfert hast - andererseits schade drum... Da ist ja selbst TMS320 Assembler eine wahre Wohltat gegen:
1 | loop:
|
2 | sovm
|
3 | ; read in and store new input value |
4 | ldpk 0 |
5 | call AD_read |
6 | ldpk 4 |
7 | sacl INPUT ; keep copy of dry input |
8 | lac *+ ; read from buffer |
9 | sacl OUTPUT ; output data |
10 | cmpr 0 |
11 | bbz nowrap2,*,AR1 |
12 | lrlk AR2,BUF |
13 | rxf
|
14 | nowrap2: |
15 | ; do level scaling on wet and add unchanged input |
16 | lt OUTPUT ; |
17 | mpy WET ; scale wet signal |
18 | apac
|
19 | addh INPUT ; get original input value |
20 | sach TMP |
21 | lac TMP ; prepare final output for AIC |
22 | ldpk 0 |
23 | sacl scratch3 ; do output |
24 | call AD_write |
25 | done: |
26 | b loop |
:
Bearbeitet durch User
@Frank M. (ukw) Benutzerseite YMMD!!!! Das ist Elektronikcomedy!
Frank M. schrieb: > Josef benutzt immer noch einen 5-Bit-Lochkartenstanzer. Sollte er etwa seinem Hex-Code untreu geworden sein?
Frank M. schrieb: > Karl Heinz schrieb: >> *CON STRG -?? Es wird eine String-Konstante mit Wert "??" vereinbart. >> *AUT AA. /00 BB. /00 Es werden die 1-Byte-Variablen AA. und BB. vereinbart. AUT steht für Automatische Installation auf Stack A beim Starten des Programms. >> 1ADDLOC /01 Erhöht den Positionszeiger für die Ein/Ausgabe. >> =DECR AA. > > Und hier dekrementieren wir wieder AA. Und jetzt das > verblüffendste: Der Wert von AA ist danach wieder ABSOLUT > IDENTISCH mit der eingegebenen Zahl!!!!!11111 Dekrementieren ist richtig, "wieder" ist falsch. Der Wert ist danach nicht identisch. Aber weil das =DECR beim erstenmal übersprungen wird, wird zuerst die unveränderte Zahl ausgegeben. >> *HERE > > Das HERE steht selbstverständlich nicht für HERE, also "hier", > sondern für "da", also THERE! Der Unterschied von here und there im Englischen ist mir durchaus klar. Aber here sieht besser aus als there, und ganz falsch ist es auch nicht, wenn man es nicht liest als "jump here", was tatsächlich falsch wäre, sondern als "hier ist das Sprungziel". >> *RP.S AA. > > Das ist ein einfacher NOP. Repeat falls AA. "set", also "not zero". >> 1SETLOC /f0 Setze Positionszeiger. >> *RP.Z BB. > > Kennen wir auch schon: Das war der NOP. Repeat falls BB. zero. >> *RETN > > Kennt jeder: RETURN. Fragt sich nur wohin? In diesem Fall in die Kommandozeile. Es wäre aber auch möglich, das Programm aus einem anderen Programm heraus aufzurufen. Alle Operationen 1xxxx sind nicht Teil der Sprache selber, sondern Teil der Software der Test-Steckkarte in Steckplatz 1. Eigenschaft der Sprache ist es aber, wie diese Operationen beim Compilieren eingebunden und bei der Programm-Ausführung aufgerufen werden.
Josef, geh in die Apotheke und kauf dir für 20 Cent Humor...
Uhu Uhuhu schrieb: > Frank M. schrieb: >> Josef benutzt immer noch einen 5-Bit-Lochkartenstanzer. > > Sollte er etwa seinem Hex-Code untreu geworden sein? Er hat einen speziellen Lochkartenstanzer dafür: Er kann eckige und runde Löcher stanzen. Damit vervielfachen sich die 5 Bits von (2^5 = 32) auf 5 Trits (3^5 Möglichkeiten = 243)! Und schon hat er Großbuchstaben, Ziffern und Punkte. Das reicht - auch für Hex-code. Der Rest ist zukünftigen Erweiterungen vorbehalten ;-)
:
Bearbeitet durch Moderator
> Er kann eckige und runde Löcher stanzen. Damit vervielfachen sich die 5 > Bits von (2^5 = 32) auf 5 Trits (3^5 Möglichkeiten = 243)! Um noch etwas genauer zu werden: Er kann die Basis über die Anzahl der Ecken einstellen. Die runden Löcher sind ganz einfach eckige Löcher mit sehr vielen Ecken. Somit hat er den Quanten-Computer erfunden. Mit der Differenzial- und der Integralrechnung kann dann noch bestimmt werden, wieviele Zyklen das Programm benötigt, um den Programmierer in die Klapse einzuliefern, nach dem er es fertiggestellt hat. ;-DDD
Frank M. (ukw) schrieb: >> *AUT AA. /00 BB. /00 > AUT steht für AUTOMATISCHE EINGABE VON ZAHLEN. Hier nennt Josef diese > AA. und BB, beide vorbelegt mit 00. Das "/" vor den Zahlen 00 soll nur > den geneigten Leser verwirren: Es handelt sich NICHT um eine Division > durch Null! Merken! Ich wollte schon fragen, ob das eine Zeile aus Josef's Fahrzeugschein ist. Sah mir ganz danach aus. Aber jetzt wo du das aufgeklärt hast ... (nicht böse gemeint Josef, bezüglich deiner Codierungen lebst du anscheinend wohl in deiner eigenen unzugänglichen Welt ;))
g. c. schrieb: > (nicht böse gemeint Josef, bezüglich deiner Codierungen lebst du > anscheinend wohl in deiner eigenen unzugänglichen Welt ;)) Ist in C ja auch nicht viel anders, wenn man es genau nimmt.
>Ist in C ja auch nicht viel anders, wenn man es genau nimmt.
Ähmm... Hochsprachen wurden ja wohl ursprünglich entwickelt, um sich die
Arbeit zu erleichtern und nicht zu erschweren, damit man sich weniger um
den ganzen Kram auf Bit-Ebene kümmern muss usw. (d.h. im Vordergrund
steht nur das Problem, das mit möglichst wenig Aufwand gelöst werden
soll), um die Lesbarkeit und Wiederverwendbarkeit des Codes zu
verbessern.
Seine kryptischen Konstrukte sind aber schwer durchzublicken.
Das sieht eher nach korruptem Assembler aus. Assembler ist sogar
einfacher zu lesen...
Achja... Eine anständige Dokumentation ist ja wohl das wichtigste, wenn
man vom Nutzen überzeugen möchte.
Ansonsten, wieso sollte ich z.B. von C/C++
umsteigen, wenn damit sowieso schon fast alles möglich ist?
Ein Vorteil der Technik ist natürlich, dass solche Programme auf
beliebigem System ablaufen können (portabilität).
Ein großer Nachteil ist, dass der erzeugte Zwischencode eben nicht nativ
ist, man für den Ablauf eines solchen Programms also unbedingt ein
Betriebssystem braucht, welches nur sehr schwer bspw. auf einen
Mikrocontroller wg. zu kleinen Speichern zu bekommen ist.
Weiterer Nachteil: Ausführungszeit ist größer als bei nativem Code.
Karl Heinz schrieb: > In normalem C wäre das ja im Grunde (wenn ich das richtig verstanden > habe) >
1 | > xyz; |
2 | >
|
3 | > while( Abbruchbedingung ) { |
4 | > abc; |
5 | > xyz; |
6 | > } |
7 | >
|
> > wobei xyz bzw. abc für irgendwelche nicht triviale Codeteile stehen. > > Solange die Codeteile nicht zu komplex sind, könnte man das auch so > schreiben >
1 | > for( xyz; Abbruchbedingung; xyz ) |
2 | > abc; |
3 | >
|
> ich finde allerdings die erste Variante mit dem while trotzdem besser.
oder halt
1 | while (xyz, bedingung) |
2 | {
|
3 | abc; |
4 | }
|
falls xyz nicht zu complex ist. Wenn schon, dann könnte man das noch in einer Funktion stecken, die die Auswertung der Bedingung zurückliefert:
1 | while (xyz_funktion()) |
2 | {
|
3 | abc; |
4 | }
|
Aber meist reicht deine erste Variante, und ein guter Compiler wird das schon optimieren.
Eric B. schrieb: > Wenn schon, dann könnte man das noch in einer Funktion > stecken, die die Auswertung der Bedingung zurückliefert Ist auch genau das, was ich geschrieben hatte: Josef G. schrieb: >> *L.JP / springt zu *HERE >> oper1 >> oper2 >> *HERE >> oper3 >> oper4 / weist VAR einen Wert zu >> *RP.Z VAR / springt zu oper1 falls VAR = zero > >> In C würde man hier eine function definieren, welche >> oper3/4 ausführt und den Rückgabewert VAR erhält. >> Und man würde eine while-Schleife verwenden, welche >> oper1/2 ausführt, solange die function Null liefert.
Danke Frank! Josef schrub > *RP.S AA. > Repeat falls AA. "set", also "not zero". > *RP.Z BB. > Repeat falls BB. zero. Ja, das ist natürlich intuitiv. S ist das Gegenteil von Z, so wie 'set' das Gegenteil von 'zero' ist. Josef, Josef. Ich denke, es wird dir immer ein unlösbares Rätsel bleiben, warum du einfach nicht den Durchbruch schaffst.
Nicht das es mich wirklich interessiert. Aber manche Zeilen fangen mit einem '*' an, manche mit einer 1, eine ist dabei, die mit einem = anfängt. Hat das irgendwas zu bedeuten, zb das die einen Zeilen nur Montags gelten und die anderen nur in Monaten mit 'R'? Oder kann man die erste Spalte einer Zeile nach Gutdünken verwenden? Es sieht ja fast so aus, als ob alle Zeilen, die irgendwas mit Flusskontrolle zu tun haben, mit einem '*' ingeleitet werden, während alle 'normalen' Anweisungen mit einer '1' beginnen, mit Ausnahme des '=' im DEC. Da wird das = wohl anzeigen, dass das Ergebnis wieder im selben Register abgelegt werden soll (was nebenbei bemerkt eigentlich recht normal wäre, sonst würde man ja auch einen SUB benutzen). Aber diese Erklärung scheint mir in Anbetracht der restlichen Komplexität als etwas zu banal. Es wäre zwar logisch, denn eigentlich braucht diese Sonderzeichen in Wirklichkeit kein Mensch, daher wäre das ein gefundenes Fressen für Josef, aber irgendwie auch wieder zu naheliegend. Da muss noch mehr dahinter stecken.
:
Bearbeitet durch User
Karl Heinz schrieb: > Da muss noch mehr dahinter stecken. http://etherealmind.com/humour-men-vs-women-machine-knobs/ s/Woman/Josef/g
Karl Heinz schrieb: > Es sieht ja fast so aus, als ob alle Zeilen, die irgendwas mit > Flusskontrolle zu tun haben, mit einem '*' ingeleitet werden, während > alle 'normalen' Anweisungen mit einer '1' beginnen, mit Ausnahme des '=' > im DEC. Da wird das = wohl anzeigen, dass das Ergebnis wieder im selben > Register abgelegt werden soll (was nebenbei bemerkt eigentlich recht > normal wäre, sonst würde man ja auch einen SUB benutzen). Ja, genau so habe ich es mir auch erklärt. Ich weiß - genauso wie Du - überhaupt nicht, was diese zusätzliche Redundanz für einen Zweck hat. Ich glaube nicht, dass Abweichungen - wie zum Beispiel =LFEED statt 1LFEED oder =RP.S AA. statt *RP.S AA. irgendwie einen Sinn ergeben. Diese Zeichen vor jedem Befehl sind irgendwie hyperfluid. Aber vielleicht ist die Erklärung ganz einfach: Wenn schon kryptisch, dann richtig! P.S. Meine Vermutung: Josefs Parser ist wahrscheinlich ziemlich einfach gestrickt. Diese Sonderzeichen unterstützen diesen wohl bei der Erkennung.
:
Bearbeitet durch Moderator
Karl Heinz schrieb: > Es sieht ja fast so aus, als ob alle Zeilen, die irgendwas mit > Flusskontrolle zu tun haben, mit einem '*' ingeleitet werden, Genauso ist es. > während alle 'normalen' Anweisungen mit einer '1' beginnen, > mit Ausnahme des '=' im DEC. Die mit 1 beginnenden Anweisungen werden von der Test-Steckkarte in Steckplatz 1 ausgeführt. Die mit = beginnenden Anweisungen sind interner Bestandteil der Sprache. Josef G. schrieb: > Alle Operationen 1xxxx sind nicht Teil der Sprache selber, > sondern Teil der Software der Test-Steckkarte in Steckplatz 1. > Eigenschaft der Sprache ist es aber, wie diese Operationen > beim Compilieren eingebunden und bei der Programm-Ausführung > aufgerufen werden.
Frank M. schrieb: > Meine Vermutung: Josefs Parser ist wahrscheinlich ziemlich einfach > gestrickt. Diese Sonderzeichen unterstützen diesen wohl bei der > Erkennung. Richtig. Bei den von den Steckkarten ausgeführten Anweisungen kommt als weiterer Vorteil hinzu, dass Namens-Konflikte vermieden werden.
Josef G. schrieb: > Die mit 1 beginnenden Anweisungen werden von der Test-Steckkarte > in Steckplatz 1 ausgeführt. Die mit = beginnenden Anweisungen > sind interner Bestandteil der Sprache. Das heisst, wenn ich de "Test-Steckkarte" in "Steckplatz 2" stecke, muss ich mein Programm neu schreiben und compilieren? o_O
Josef G. schrieb: > Die mit 1 beginnenden Anweisungen werden von der Test-Steckkarte > in Steckplatz 1 ausgeführt. D.h.: wenn du deine Steckkarten mischst, läuft das Programm nicht mehr. Das ist eine echte Innovation, nach der Heere von Informatikern seit 60 Jahren gesucht haben, aber nie fündig wurden.
Josef G. schrieb: > Frank M. schrieb: >> Meine Vermutung: Josefs Parser ist wahrscheinlich ziemlich einfach >> gestrickt. Diese Sonderzeichen unterstützen diesen wohl bei der >> Erkennung. > > Richtig. Es gibt von Nikolaus Wirth ein kleines Büchlein über Compilerbau. Das ist auch für Nicht-Informatiker verständlich. Ich würde dir den Erwerb und das Durcharbeiten desselben extremst ans Herz legen.
Eric B. schrieb: > Das heisst, wenn ich de "Test-Steckkarte" in "Steckplatz 2" stecke, muss > ich mein Programm neu schreiben und compilieren? o_O Nein. Josef G. schrieb: > Auf die Karten > wird über die Steckplatz-Nummer zugegriffen, das erfolgt indirekt > über eine Ersetzungstabelle. Wenn man eine Karte in einen anderen > Steckplatz steckt, muss man nur diese Tabelle ändern. Ergänzend gibt es ein Kommando zur Vertauschung von Steckplatz-Nummern in fertig kompilierten Programmen.
Frank M. schrieb: > Meine Vermutung: Josefs Parser ist wahrscheinlich ziemlich einfach > gestrickt. Yacc gibts seit 1975. ;-)
Uhu Uhuhu schrieb: > Josef G. schrieb: >> Die mit 1 beginnenden Anweisungen werden von der Test-Steckkarte >> in Steckplatz 1 ausgeführt. > > D.h.: wenn du deine Steckkarten mischst, läuft das Programm nicht mehr. > > Das ist eine echte Innovation, nach der Heere von Informatikern seit 60 > Jahren gesucht haben, aber nie fündig wurden. Allerdings. Das ist mal was Neues. Normalerweise versucht man ja um jeden Preis, so einen Zustand zu vermeiden. Wie weiter oben schon mal wer geschrieben hat: Wenn schon komplex, dann richtig komplex.
A. K. schrieb: > Yacc gibts seit 1975. ;-) Ja, hat mir schon früher immer viel Spaß gemacht, mit yacc einen Parser zu bauen. Leider ist yacc aber für User, die weniger bis gar nicht mit unixoiden Systemen arbeiten, ziemlich unbekannt. Mittlerweile bin ich davon aber weg und habe meine eigenen Libs, um Sprachen zu entwickeln und diese zu parsen.
Fall jemand denkt, Compiler müssten unbedingt riesige und komplexe Gebilde sein: Im Anhang ein einfacher Compiler für Z80 und ein Beispielprogramm, Quelle und Resultat. Die Sprache erinnert eher an einen strukturierten Assembler, aber das war Absicht und ist hier im Thread ja auch nicht anders.
:
Bearbeitet durch User
Karl Heinz schrieb: > Normalerweise versucht man ja um jeden Preis, so einen Zustand zu > vermeiden. Die Aussage ist ja nach meinem obigen Beitrag wohl hinfällig. Beitrag "Re: Gibt es eine Programmiersprache mit diesem Schleifentyp?"
Frank M. schrieb: > Ja, hat mir schon früher immer viel Spaß gemacht, mit yacc einen Parser > zu bauen. […] > > Mittlerweile bin ich davon aber weg Yacc und Bison sind mittlerweile ziemlich aus der Mode gekommen. Um sie für nichtriviale Dinge benutzen zu können, muss man wissen, wie ein LALR-Parser-funktioniert und wie er durch den Parsergenerator erzeugt wird. Nur dann können die Fehlermeldungen richtig gedeutet und die angezeigten Konflikte in ihrer Relevanz bewertet werden. Wenn jemand aber so viel Wissen in sich vereint, ist es für ihn ein Leichtes, einen Parser von Hand zu schreiben (ggf. unter Zuhilfenahme einer entsprechenden Bibliothek). Das mag zwar etwas mehr Tipparbeit bedeuten als wenn ein Generator zum Einsatz kommt. Da ein Compiler aber nicht nur aus dem Parser besteht, sondern der Parser nur einen kleinen Teil des Ganzen ausmacht, fällt dieser Mehraufwand praktisch überhaupt nicht ins Gewicht und wird durch die größere Flexibilität, die man mit einem handgeschrieben Parser hat, mehr als wett gemacht.
Yalu X. schrieb: > Da ein Compiler aber > nicht nur aus dem Parser besteht, sondern der Parser nur einen kleinen > Teil des Ganzen ausmacht, Mein vorhin gezeigter Compiler besteht praktisch nur aus dem Parser. ;-) Der Code kommt direkt aus dem Parser raus, keine Zwischendarstellung. Mehr als dieses eine File gibts nicht, das ist der ganze Compiler. Sicher, für ernsthafte und grosse Compiler ist das nicht unbedingt der beste Weg. Aber für einfache kleine Dinge ist das ungemein praktisch. So hatte ich auch mal einen Dekoder für Intel/Microsoft-Objektfiles gebaut, und einen Encoder in Yacc dazu, der den Output des Dekoders wieder zu einem Objektfile zusammen baute. Um solche Files in Textform einfach modifizieren zu können.
:
Bearbeitet durch User
A. K. schrieb: > Mein vorhin gezeigter Compiler besteht praktisch nur aus dem Parser. ;-) und > Aber für einfache kleine Dinge ist das ungemein praktisch. Zweimal Zustimmung :) Für die kleinen Dinge, wo auch die Übersetzungsgeschwindigkeit nicht die große Rolle spielt, kann man sich auch mal Pyparsing anschauen: http://pyparsing.wikispaces.com/ Wenn man es ernsthaft benutzen möchte, sollte man aber das auf der Webseite angepriesene Buch kaufen. Die Online-Dokumentation ist eher als ergänzende Referenz dazu zu sehen. Edit: Nein, man muss es nicht kaufen (ist auch gar nicht so leicht irgendwo zu bekommen). Hier kann man es online lesen oder herunterladen: http://it-ebooks.info/book/245/
:
Bearbeitet durch Moderator
Yalu X. schrieb: > Yacc und Bison sind mittlerweile ziemlich aus der Mode gekommen. Um sie > für nichtriviale Dinge benutzen zu können, muss man wissen, wie ein > LALR-Parser-funktioniert und wie er durch den Parsergenerator erzeugt > wird. Nur dann können die Fehlermeldungen richtig gedeutet und die > angezeigten Konflikte in ihrer Relevanz bewertet werden. Was ich an Yacc nie mochte, ist genau dieser LALR Parser. Ich hatte damit immer Schwierigkeiten in der Grammatik-Definition. Ok, das mag auch an meiner Ausbildung liegen, in der wir im Compilerbau uns nur auf LL(1) Parser konzentriert haben. Von daher war ich es gewohnt, bereits die Grammatik auf LL(1) Verhalten zu trimmen, was ich ehrlich gesagt nie als Nachteil empfand. Gerade mit Yacc hatte ich bei den wenigen Versuchen auch immer das Problem, dass ich die Statemachine als sehr schwierig zu debuggen empfand. Etwas, das mir bei einem rekursiven Abstieg überhaupt keine Probleme machte. Von daher wurde dann der COCO zu meinem Lieblings Parser Generator. Auch wenn es den meines Wissens auch als tabellenbasierenden Parser gibt, bevorzuge ich trotzdem die Variante, die einen rekursiven Abstieg erzeugt. Im Debuggen ist das meiner Meinung nach immer noch unschlagbar einfach, wenn man sich wieder mal in der Grammatik einen Hund reingehauen hat. > nicht ins Gewicht und wird durch die größere Flexibilität, die man mit > einem handgeschrieben Parser hat, mehr als wett gemacht. Da ich sowieso auch per Hand einen rekursiven Abstieg schreiben würde, lass ich mir diese Arbeit gerne von einem Generator abnehmen :-) Der macht dann nicht die vielen kleinen Fehlerchen, die ich selbst bei sorgfältigstem Arbeiten einbauen würde (wieder mal ein nextSym Aufruf versemmelt, so was kann einem den ganzen Tag versauen :-)
:
Bearbeitet durch User
Karl Heinz schrieb: > Ok, das mag > auch an meiner Ausbildung liegen, in der wir im Compilerbau uns nur auf > LL(1) Parser konzentriert haben. Das ist ja auch die verkehrte Reihenfolge ;-). Ich hatte mich dank entsprechendem Bedarf zuerst mit Compiler und Yacc beschäftigt. Die Vorlesung "Compilerbau" folgte später. PS: Das war in den 80ern. Da war die Auswahl solcher Werkzeuge noch etwas kleiner als heute. Auch der PLZ Compiler entstand damals, wie man unschwer am K&R Code erkennen kann. Es liegt in der Natur der Sache, dass über die Jahrzehnte bessere Werkzeuge entstehen.
:
Bearbeitet durch User
Yalu X. schrieb: > Noch etwas zum Thema Schleifenoptimierung: Der GCC dreht alle Schleifen > – egal, ob for, while, do-while oder endlos mit break – so hin, dass die > Abbruchbedingung am Ende liegt. Man muss sich darum als Programmierer > also keine Gedanken machen. Jede while-Schleife ist also in Wahrheit eine Schleife mit Hineinsprung. Dies wird lediglich vor dem Programmierer verborgen.
Josef G. schrieb: > Jede while-Schleife ist also in Wahrheit eine Schleife mit Hineinsprung. Nein. "Hinein" im Sinne von "Nicht an den Anfang" wird in eine while-Schleife nicht.
@Josef G. (bome) Benutzerseite >Jede while-Schleife ist also in Wahrheit eine Schleife mit Hineinsprung. >Dies wird lediglich vor dem Programmierer verborgen. Nö. Es wird nur vor dem Schleifeneintritt die Laufbedingung geprüft. Deine Diskussion ist eine Endlosschleife ohne Heraussprung, komplementär zu deiner erfundenen Schleife mit Hineinsprung.
Yalu X. schrieb: > Noch etwas zum Thema Schleifenoptimierung: Der GCC dreht alle Schleifen > – egal, ob for, while, do-while oder endlos mit break – so hin, dass die > Abbruchbedingung am Ende liegt. Man muss sich darum als Programmierer > also keine Gedanken machen. Rein Interessehalber: Das habe ich jetzt schon mehrfach gelesen. Andererseits hat einer meiner Profs behauptet, es würde einen Unterschied machen (dass der möglicherweise auf dem Stand der 80er stehen geblieben ist, schließe ich ausdrücklich nicht aus). Hat jemand eine vertrauenswürdige Quelle oder sogar ein Schnipsel Beispielcode mit zugehörigem Auszug aus der Ausgabe des Compilers, wodurch das bestätigt wird? Leider bin ich mit Google nicht fündig geworden und konnte auch selbst kein aussagekräfgiges Beispiel herbeizaubern. Letzteres liegt aber wahrscheinlich daran, dass meine Assembler-Kenntinsse auf dem Stand "ich weiß was es ist" sind... Dass es in der Praxis zu 99,99% irrelevant ist, ist mir auch klar. Aber besagter Prof hat dazu auch eine Prüfungsfrage gestellt und ist voll davon überzeugt, deswegen interessier es mich eben doch ;-)
Andreas P. schrieb: > Hat jemand eine vertrauenswürdige Quelle oder sogar ein Schnipsel > Beispielcode mit zugehörigem Auszug aus der Ausgabe des Compilers, > wodurch das bestätigt wird? Kannst du nicht einfach selbst ein Stück Code compilieren? Nimm doch eine einfache Kopierschleife:
1 | void
|
2 | memorycopy(char *dst, const char *src, unsigned int i) |
3 | {
|
4 | for (unsigned j = 0; j < i; j++) { |
5 | *dst++ = *src++; |
6 | }
|
7 | }
|
Compiliert auf dem Host (amd64) mit -Os gibt das:
1 | .file "copy.c" |
2 | .text |
3 | .globl memorycopy |
4 | .type memorycopy, @function |
5 | memorycopy: |
6 | .LFB0: |
7 | .cfi_startproc |
8 | xorl %eax, %eax |
9 | .L2: |
10 | cmpl %eax, %edx |
11 | jbe .L5 |
12 | movb (%rsi,%rax), %cl |
13 | movb %cl, (%rdi,%rax) |
14 | incq %rax |
15 | jmp .L2 |
16 | .L5: |
17 | ret |
18 | .cfi_endproc |
19 | .LFE0: |
20 | .size memorycopy, .-memorycopy |
21 | .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" |
22 | .section .note.GNU-stack,"",@progbits |
Der Schleifentest befindet sich hier am Anfang der Schleife, beim Erreichen des Schleifenendes wird zu .L5 rausgesprungen, am Ende der Schleife wird zum Test nach .L2 zurückgesprungen. Gleicher Code mit dem AVR-GCC compiliert:
1 | .file "copy.c" |
2 | __SP_H__ = 0x3e |
3 | __SP_L__ = 0x3d |
4 | __SREG__ = 0x3f |
5 | __tmp_reg__ = 0 |
6 | __zero_reg__ = 1 |
7 | .text |
8 | .global memorycopy |
9 | .type memorycopy, @function |
10 | memorycopy: |
11 | /* prologue: function */ |
12 | /* frame size = 0 */ |
13 | /* stack size = 0 */ |
14 | .L__stack_usage = 0 |
15 | mov r30,r22 |
16 | mov r31,r23 |
17 | add r20,r24 |
18 | adc r21,r25 |
19 | rjmp .L2 |
20 | .L3: |
21 | ld r18,Z+ |
22 | mov r26,r24 |
23 | mov r27,r25 |
24 | st X+,r18 |
25 | mov r24,r26 |
26 | mov r25,r27 |
27 | .L2: |
28 | cp r24,r20 |
29 | cpc r25,r21 |
30 | brne .L3 |
31 | /* epilogue start */ |
32 | ret |
33 | .size memorycopy, .-memorycopy |
34 | .ident "GCC: (GNU) 4.7.2" |
Hier wird zuerst (bedingungslos) zum Schleifentest am Ende der Schleife nach .L2 gesprungen, bei negativem Test dann zurück zum Anfang nach .L3. Bleibt sich völlig gleich, was der Compiler genau draus macht.
Jörg Wunsch schrieb: > Kannst du nicht einfach selbst ein Stück Code compilieren? Erst mal danke für deine Antwort! Das heißt, das ganze ist absolut vom Compiler abhängig und eine Aussage von wegen "das eine ist besser" ist scheinbar sinnlos, ohne die Toolchain und deren Einstellung sehr genau zu kennen (und anzugeben)... Das Problem, das ich habe ist folgendes: Aus diesem Code mit for()
1 | int main(void) |
2 | {
|
3 | int b=0; |
4 | for (int i=1; i<10; i*2) |
5 | {
|
6 | asm volatile ("nop"); |
7 | b++; |
8 | }
|
9 | }
|
macht Atmel Studio folgendes:
1 | 000000c4 <main>: |
2 | int main(void) |
3 | { |
4 | int b=0; |
5 | for (int i=1; i<10; i*2) |
6 | { |
7 | asm volatile ("nop"); |
8 | c4: 00 00 nop |
9 | c6: fe cf rjmp .-4 ; 0xc4 <main> |
und aus dem Beispiel mit while() hier:
1 | int main(void) |
2 | {
|
3 | int b=0; |
4 | int i=1; |
5 | while(i<10) |
6 | {
|
7 | i=i*2; |
8 | asm volatile ("nop"); |
9 | b++; |
10 | }
|
11 | }
|
wird das hier:
1 | 000000c4 <main>: |
2 | |
3 | |
4 | #include <avr/io.h> |
5 | |
6 | int main(void) |
7 | { |
8 | c4: 84 e0 ldi r24, 0x04 ; 4 |
9 | c6: 90 e0 ldi r25, 0x00 ; 0 |
10 | int b=0; |
11 | int i=1; |
12 | while(i<10) |
13 | { |
14 | i=i*2; |
15 | asm volatile ("nop"); |
16 | c8: 00 00 nop |
17 | ca: 01 97 sbiw r24, 0x01 ; 1 |
18 | |
19 | int main(void) |
20 | { |
21 | int b=0; |
22 | int i=1; |
23 | while(i<10) |
24 | cc: 00 97 sbiw r24, 0x00 ; 0 |
25 | ce: e1 f7 brne .-8 ; 0xc8 <main+0x4> |
26 | { |
27 | i=i*2; |
28 | asm volatile ("nop"); |
29 | b++; |
30 | } |
31 | d0: 80 e0 ldi r24, 0x00 ; 0 |
32 | d2: 90 e0 ldi r25, 0x00 ; 0 |
33 | d4: 08 95 ret |
Also für meine (in Assembler ungeübten) Augen ein komplett anderer und vor Allem deutlich umfangreierer Code...also macht es scheinbar doch einen Unterschied? Oder spielt mir hier die Optimierung des Compilers einen Streich? Insbesondere verwirrt mich die doppelte main-Schleife beim Beispiel mit while() (und nein, mir ist nicht beim kopieren ein Fehler unterlaufen ;-) ) Beide Beispiele sind mit absolut gleichen Einstellungen gleichzeitig kompiliert worden.
Jörg Wunsch schrieb: > Hier wird zuerst (bedingungslos) zum Schleifentest am Ende der Schleife > nach .L2 gesprungen, bei negativem Test dann zurück zum Anfang nach .L3. Das bestätigt meine Aussage: Josef G. schrieb: > Jede while-Schleife ist also in Wahrheit eine Schleife mit Hineinsprung. > Dies wird lediglich vor dem Programmierer verborgen.
Andreas P. schrieb: > Also für meine (in Assembler ungeübten) Augen ein komplett anderer und > vor Allem deutlich umfangreierer Code. Klar, wenn der Quellcode was anderes tut … Hast du dir mal die Warnungen angesehen? Da sollte drin stehen:
1 | foo.c: In function ‘main’: |
2 | foo.c:4:5: warning: statement with no effect [-Wunused-value] |
Dein „b * 2“ in der for-Schleife ist das, was er damit meint: das ist eine sinnlose Operation. Vermutlich meintest du entweder „b *= 2“ oder „b = b * 2“. Josef G. schrieb: > Das bestätigt meine Aussage: Aber nur, weil du dein selektives Wahrnehmungsfilter eingeschaltet hast. Ansonsten wäre dir aufgefallen, dass der völlig gleiche Sourcecode selbst vom nahezu gleichen Compiler (GCC 4.7 vs. 4.8) nur für eine andere Zielarchitektur halt genau andersherum compiliert wird: erst der Test, dann die Schleife. Aus Sicht der Programmiersprache ist dein Konstrukt also absolut überflüssig, da er nichts anderes als die normale while-Schleife darstellt, die man halt verschieden in Maschinencode umsetzen kann. Auch eine dritte Variante ist noch drin, wenn man nämlich auf maximale Geschwindigkeit optimieren lässt. Dann wird der Test auf vorzeitigen Schleifenabbruch vorgezogen und ein zweiter Test am Ende der Schleife eingebaut. Hier das Beispiel mit dem AVR-GCC:
1 | .file "copy.c" |
2 | __SP_H__ = 0x3e |
3 | __SP_L__ = 0x3d |
4 | __SREG__ = 0x3f |
5 | __tmp_reg__ = 0 |
6 | __zero_reg__ = 1 |
7 | .text |
8 | .global memorycopy |
9 | .type memorycopy, @function |
10 | memorycopy: |
11 | /* prologue: function */ |
12 | /* frame size = 0 */ |
13 | /* stack size = 0 */ |
14 | .L__stack_usage = 0 |
15 | mov r30,r24 |
16 | mov r31,r25 |
17 | cp r20,__zero_reg__ |
18 | cpc r21,__zero_reg__ |
19 | breq .L1 |
20 | mov r26,r22 |
21 | mov r27,r23 |
22 | add r20,r24 |
23 | adc r21,r25 |
24 | .L3: |
25 | ld r24,X+ |
26 | st Z+,r24 |
27 | cp r30,r20 |
28 | cpc r31,r21 |
29 | brne .L3 |
30 | .L1: |
31 | ret |
32 | .size memorycopy, .-memorycopy |
33 | .ident "GCC: (GNU) 4.7.2" |
:
Bearbeitet durch Moderator
Jörg Wunsch schrieb: >> Also für meine (in Assembler ungeübten) Augen ein komplett anderer und >> vor Allem deutlich umfangreierer Code. > > Klar, wenn der Quellcode was anderes tut … Hast du dir mal die > Warnungen angesehen? Da sollte drin stehen: > foo.c: In function ‘main’: > foo.c:4:5: warning: statement with no effect [-Wunused-value] > > Dein „b * 2“ in der for-Schleife ist das, was er damit meint: das > ist eine sinnlose Operation. > > Vermutlich meintest du entweder „b *= 2“ oder „b = b * 2“. Kopf --> Tisch Du hast ja so was von recht (obwohl du mit b das i meinst). Sorry, mein Fehler! Wenns keine Probleme zwischen Tastatur und Stuhl gibt, kommt tatsächlich genau der gleiche Asm-Code raus ;-) Übrigens auch wenn man es über do-while, for-break und while-break realisiert. Danke für deine Hilfe und vy 73!
Andreas P. schrieb: > (dass der möglicherweise auf dem Stand der 80er > stehen geblieben ist, schließe ich ausdrücklich nicht aus). Diesen Trick hatte man schon in der 80ern drauf. Weils eine der einfachsten Übungen ist. Komplexer wird es eher heute wieder, weil das Verhalten der Prozessoren und deren Sprünge viel komplexer geworden ist.
:
Bearbeitet durch User
Jörg Wunsch schrieb: > Aus Sicht der Programmiersprache ist dein Konstrukt also absolut > überflüssig, da er nichts anderes als die normale while-Schleife > darstellt, die man halt verschieden in Maschinencode umsetzen kann. Das Konstrukt wäre überflüssig, wenn die Sprache zusätzlich while-Schleifen bietet. Es ist jedoch nicht überflüssig, wenn es keine while-Schleifen gibt und diese durch eben dieses Konstrukt ersetzt werden. Und gegenüber while-Schleifen hat das Konstrukt den Vorteil, dass man bereits im Quelltext unmittelbar den erzeugten Code vor sich sieht und keine Optimierung erforderlich ist. Ausserdem hat es den Vorteil, dass die Ermittlung der Abbruchbedingung beliebig kompliziert sein kann. Es muss lediglich die Zielmarke *HERE des Hineinsprungs entsprechend weit genug vor dem bedingten Rückwärtssprung liegen. Bei der while-Schleife müsste man in so einem Fall die Ermittlung der Abbruchbedingung in eine function packen, deren Rückgabewert dann abgefragt wird. Oder man muss eine Endlosschleife mit bedingtem break verwenden, was der Philosophie der while-Schleife zuwiderläuft.
Jörg Wunsch schrieb: > Der Schleifentest befindet sich hier am Anfang der Schleife, beim > Erreichen des Schleifenendes wird zu .L5 rausgesprungen, am Ende > der Schleife wird zum Test nach .L2 zurückgesprungen. Wobei das am -Os hängt. Bei -O1 gibts eine dritte Version: if (condition) do ... while (condition) Also keine eingesprungene Schleife, sondern eine vermiedene Schleife. testl %edx, %edx je .L1 movl $0, %eax .L3: movzbl (%rsi,%rax), %ecx movb %cl, (%rdi,%rax) addq $1, %rax cmpl %eax, %edx ja .L3 .L1:
Josef G. schrieb: > Das Konstrukt wäre überflüssig, wenn die Sprache zusätzlich > while-Schleifen bietet. Es ist jedoch nicht überflüssig, wenn > es keine while-Schleifen gibt und diese durch eben dieses > Konstrukt ersetzt werden. OK. > Und gegenüber while-Schleifen hat das Konstrukt den Vorteil, > dass man bereits im Quelltext unmittelbar den erzeugten Code > vor sich sieht und keine Optimierung erforderlich ist. Das ist kein Vorteil, sondern deutet darauf hin, dass du eher eine Art „Hochsprach-Assembler“ im Blick hast denn einen vernünftigen Compiler. Letzterer sollte immer sinnvoll optimieren können, und siehe oben, ob nun der Schleifentest am Anfang oder am Ende oder auf beiden Seiten erfolgt, kann von Fall zu Fall unterschiedlich optimiert werden. Aus Sicht des Programmierers wäre es mir wichtig, dass die Programmiersprache sich einigermaßen so verhält wie die bereits existierenden Sprachen (POLA - principle of least astonishment). > Ausserdem hat es den Vorteil, dass die Ermittlung der > Abbruchbedingung beliebig kompliziert sein kann. Inwiefern sollte das bei existierenden while-Schleifen nicht der Fall sein? Aber lassen wir das: keiner außer dir versteht deine etwas verworren anmutenden Gedankengänge, während Millionen von Programmierern es bislang geschafft haben, mit klassischen while-Schleifen ohne große Diskussionen ihr Ziel zu erreichen. A. K. schrieb: > Wobei das am -Os hängt. Bei -O1 gibts eine dritte Version: Ja, das ist die, die er auch bie -O3 dann noch nimmt.
Josef G. schrieb: > Und gegenüber while-Schleifen hat das Konstrukt den Vorteil, > dass man bereits im Quelltext unmittelbar den erzeugten Code > vor sich sieht und keine Optimierung erforderlich ist. Du verkaufst also einen Mangel (Sprache bietet keine while-Schleife) als einen Vorteil? Das erinnert mich an Versicherungsvertreter. Eine höhere Programmiersprache bietet eine Abstraktionsebene an. Genau das ist der Sinn einer höheren Programmiersprache. Dem Programmierer kann es im allgemeinen vollkommen wurscht sein, wie der Compiler das umsetzt. Hauptsache, er macht es korrekt und effizient. Und jetzt lassen wir uns nochmal Deinen Satz auf der Zunge zergehen: > Und gegenüber while-Schleifen hat das Konstrukt den Vorteil, > dass man bereits im Quelltext unmittelbar den erzeugten Code > vor sich sieht und keine Optimierung erforderlich ist. Der einzige Programmierer, der "unmittelbar den erzeugten Code vor sich sieht", ist ein Assembler-Programmierer. Deine "Sprache" bietet also keine Abstraktionsebene an. Und das soll ein "Vorteil" sein? Oder noch besser im Umkehrschluss: Alle Sprachen, die dem Programmierer Dein "Konstrukt" nicht zur Verfügung stellen, stellen einen Rückschritt dar?
:
Bearbeitet durch Moderator
Josefs Version der eingesprungenen Schleife: while condition statement2 entry: statement1 end while Äquivalente Version in vielen Programmiersprachen: while true statement1 if condition false exit while statement2 end while Wie der Compiler das in Code umsetzt ist sowieso seine Sache, d.h. aus dem Quellcode der zweiten Version kann ein an die erste Version erinnernder Maschinencode werden, oder es kann streckenweise conditional execution genutzt werden. Die zweiten Version hat den Vorzug, dass nicht einen Block rein, sondern aus ihm raus gesprungen wird. Das ist an vielen Stellen angenehmer, beispielsweise bei Variablen lokal zum Block (*) und bei exception handling. Optisch vermeidet man in der zweiten den Spaghetticode-Eindruck der ersten Version, deren Statements anfangs in umgekehrter Reihenfolge ausgeführt werden. Ein Compiler würde in der heute üblichen Datenflussanalyse die erste Version deshalb ohnehin intern umdrehen müssen. *: Mit lokaler Variable, wird das dann so geschrieben: while condition statement2(i) // was ist i? Kenn ich nicht! entry: declare i as integer = 1 i = 2 end while oder so: while condition declare i as integer = 1 // wird i hier initialisiert? statement2(i) // Aufruf ist mit i=2 oder i=1? entry: // wird i hier initialisiert? i = 2 end while oder wie? Eingesprungene Blöcke sind Mist. Die C Syntax von switch statements ist es auch.
:
Bearbeitet durch User
@ A. K. (prx) >Eingesprungene Blöcke sind Mist. Na dann vielleicht eingesprungene Böcke? http://upload.wikimedia.org/wikipedia/commons/b/b7/Saut_de_gazelle_Springbok.jpg > Die C Syntax von switch statements ist es auch. Warum? Das ist doch sonnenklar, oder?
Falk Brunner schrieb: > Na dann vielleicht eingesprungene Böcke? Die sind zwar kein Mist, aber machen Mist.
A. K. schrieb: > beispielsweise bei Variablen lokal zum Block (*) Gibt es in meiner Sprache nicht. Gegf. müsste die Variablen-Vereinbarung nach *HERE erfolgen. > Spaghetticode-Eindruck Wenn die Schleife mit Hineinsprung auf dich wie Spaghetticode wirkt, ist das rein subjektiv und nur eine Frage der Gewöhnung. Tatsächlich hat es mit Spaghetticode nichts zu tun. Josef G. schrieb: >> Warum man die "strenge Blockstruktur" anschließend wieder mit so einer >> komischen Konstruktion, wie dem "Hineinsprung" durchlöchern muss, > > Die wird nicht durchlöchert. > L.JP .. HERE .. RP.x ist genauso ein Block mit zwei Unterblöcken, > wie dies bei if .. else .. end-else der Fall ist.
Josef G. schrieb: > Wenn die Schleife mit Hineinsprung auf dich wie Spaghetticode > wirkt, ist das rein subjektiv und nur eine Frage der Gewöhnung. Totschlagargument: kann man immer behaupten. „Für mich sieht das alles schön aus, muss also dein Problem sein.“ Wie ich schon schrieb: mach' deine Sprache, wenn du sie unbedingt so brauchst. Alles andere scheint dich ja nur dann zu interessieren, wenn es irgendwie deiner Argumentation hilfreich ist, also als externe Selbstbestätigung.
Jörg Wunsch schrieb: > Aus Sicht des Programmierers wäre es mir wichtig, dass die > Programmiersprache sich einigermaßen so verhält wie die bereits > existierenden Sprachen (POLA - principle of least astonishment). naja, es gibt auch Programmierer, die wünschen sich POFUC (Principle Of FUrthest from C).
A. K. schrieb: > Josefs Version der eingesprungenen Schleife: > while condition > statement2 > entry: > statement1 > end while Abgesehenen davon, dass die Schlüsselworte bei mir anders sind: Die Verwirrung entsteht durch die von dir gewählte Bezeichnung statement2 statement1. Ich würde es so schreiben:
1 | *L.JP |
2 | statement1 |
3 | *HERE |
4 | statement2 |
5 | RP.c |
In der Schleife wird jeweils statement1 & statement2 ausgeführt, zuletzt statement2. Nur beim ersten Durchlauf wird statement1 übersprungen. Wenn man diese Vorstellung verinnerlicht hat, wirkt alles ganz natürlich. Wie schon geschrieben: reine Gewöhnungssache.
Josef G. schrieb: > Wenn man diese Vorstellung verinnerlicht hat, wirkt alles > ganz natürlich. Gewiss.
Jörg Wunsch schrieb: >> Ausserdem hat es den Vorteil, dass die Ermittlung der >> Abbruchbedingung beliebig kompliziert sein kann. > > Inwiefern sollte das bei existierenden while-Schleifen > nicht der Fall sein? Es ist jedenfalls nicht so einfach wie bei der Schleife mit Hineinsprung.
Josef G. schrieb: > Es ist jedenfalls nicht so einfach wie > bei der Schleife mit Hineinsprung. Kann ich nicht nachvollziehen. Ich kann zwischen den beiden oben gezeigten Varianten keinen Unterschied erkennen, was die Komplexität der Abbruchbedingung angeht.
:
Bearbeitet durch User
Josef G. schrieb: > Wenn man diese Vorstellung verinnerlicht hat, wirkt alles > ganz natürlich. Wie schon geschrieben: reine Gewöhnungssache. Ich fand es auch mal völlig natürlich, das bei -1+2 am Ende -3 rauskommt, und 1/3 zu 3 wird. Ersteres finde ich auch heute noch logischer und konsequenter als den ganzen Zirkus mit der operator precedence.
:
Bearbeitet durch User
A. K. schrieb: > Kann ich nicht nachvollziehen. Josef G. schrieb: > Ausserdem hat es den Vorteil, dass die Ermittlung der > Abbruchbedingung beliebig kompliziert sein kann. Es muss > lediglich die Zielmarke *HERE des Hineinsprungs entsprechend > weit genug vor dem bedingten Rückwärtssprung liegen. > > Bei der while-Schleife müsste man in so einem Fall die > Ermittlung der Abbruchbedingung in eine function packen, > deren Rückgabewert dann abgefragt wird. Oder man muss > eine Endlosschleife mit bedingtem break verwenden, > was der Philosophie der while-Schleife zuwiderläuft.
Ich bezog mich die zu deiner Methode äquivalente Version in Beitrag "Re: Gibt es eine Programmiersprache mit diesem Schleifentyp?".
A. K. schrieb: > Ich bezog mich die zu deiner Methode äquivalente Version in > Beitrag "Re: Gibt es eine Programmiersprache mit diesem Schleifentyp?". A. K. schrieb: > Äquivalente Version in vielen Programmiersprachen: > while true > statement1 > if condition false > exit while > statement2 > end while Ich vermute, gemeint war das: > while true > statement1 > if condition=false exit > statement2 > end while also eine Endlosschleife mit bedingtem break. Da gilt meine Aussage > eine Endlosschleife mit bedingtem break verwenden, > was der Philosophie der while-Schleife zuwiderläuft. Ist sicher kein starkes Argument, aber immerhin, es ist ein Argument. Bei einfacher Abbruchbedingung schreibt man while(??){...} Wenn die Ermittlung der Abbruchbedingung eine lange Befehlsfolge beinhaltet, schreibt man while(1){Befehlsfolge; if(??) break; ...} Man hat also zwei verschiedene Strukturen, je nach Komplexität der Abbruchbedingung. Bei der Schleife mit Hineinsprung hat man beide Male dieselbe Struktur, nur die Marke *HERE steht unterschiedlich weit vom bedingen Rückwärtssprung entfernt.
:
Bearbeitet durch User
Josef G. schrieb: > Wenn die Ermittlung der Abbruchbedingung > eine lange Befehlsfolge beinhaltet, schreibt man > > while(1){Befehlsfolge; if(??) break; ...} Warum denn das? Alles, was du im "if" schreiben kannst, kannst du auch im "while" als Bedingung schreiben. Klar kann es aus Gründen der Übersichtlichkeit Sinn haben, dass man für die Bedingung eine (inline-)Funktion schreibt, aber ansonsten kann man gerade in C beliebig komplexe Ausdrücke als Steuerung von "if" oder "while" konstruieren. Du legst dir tausendundein Argument zurecht für einen Konstrukt, den nur du haben willst, und den du nun unbedingt allen anderen als Vorteil deines aufgebohrten Assemblers verkaufen willst. Aufgebohrte Assembler gab es auch schon vor 40 Jahren, PL/M zum Bleistift. Sie haben sich nicht wirklich durchgesetzt.
Jörg Wunsch schrieb: >> Und gegenüber while-Schleifen hat das Konstrukt den Vorteil, >> dass man bereits im Quelltext unmittelbar den erzeugten Code >> vor sich sieht und keine Optimierung erforderlich ist. > > Das ist kein Vorteil, sondern deutet darauf hin, dass du eher eine Es hat sogar einen praktischen Vorteil: Ein Programmierer, der sich das Compilat anschauen will um es zu überprüfen, findet sich schneller zurecht.
Jörg Wunsch schrieb: > Alles, was du im "if" schreiben kannst, kannst du auch im "while" > als Bedingung schreiben. So etwas? while(Befehlsfolge; ??){...} Wusste ich nicht, dass das geht.
@ Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite >Wie ich schon schrieb: mach' deine Sprache, wenn du sie unbedingt so >brauchst. Alles andere scheint dich ja nur dann zu interessieren, >wenn es irgendwie deiner Argumentation hilfreich ist, also als >externe Selbstbestätigung. Genau. Darum sind wir HIER und JETZT Zeuge der Geburt einer neuen Sprache. https://www.youtube.com/watch?v=XrzRH2I5Mpo E (wie Ego), created by Josef G. (Über G. dürfen hier und jetzt pupertäre Witzchen gemacht werden ;-)
Josef G. schrieb: > Wusste ich nicht, dass das geht. Du kennst C nicht, insbesondere den Komma-Operator. Jetzt verrätst du mir aber, warum man so einen Kram unbedingt vor der ersten Iteration der Schleife ernsthaft braucht. In allen anderen Fällen kann man ja deine „Befehlsfolge“ problemlos ans Ende der Schleife schreiben, ohne dass sie beim ersten Mal mit abgearbeitet werden muss. Josef G. schrieb: > Es hat sogar einen praktischen Vorteil: Ein Programmierer, > der sich das Compilat anschauen will um es zu überprüfen, > findet sich schneller zurecht. Nur, weil dein Compiler zu blöd ist zum optimieren. Bei einem optimierenden Compiler erkennt man völlig unabhängig von solchen Schleifenkonstrukten im Maschinencode niemals mehr 1:1 den Quellcode. Aus dem Alter, in dem man das Compilat „überprüfen“ muss, sind wir sowieso schon lange raus.
:
Bearbeitet durch Moderator
Ist das Compilat gar desolat, weiß meistens nur der Josef Rat. ;-)
Jörg Wunsch schrieb: > Josef G. schrieb: >> Wenn die Ermittlung der Abbruchbedingung >> eine lange Befehlsfolge beinhaltet, schreibt man >> >> while(1){Befehlsfolge; if(??) break; ...} > > Warum denn das? > ... kannst du auch im "while" als Bedingung schreiben. Jörg Wunsch schrieb: > Du kennst C nicht, insbesondere den Komma-Operator. Wenn aber "Befehlsfolge" keine einfache Folge ist, sondern selber Verzweigungen/Schleifen beinhaltet? Sogar wenn es ginge, das in den runden Klammern der Struktur
1 | while(..., ..., ??){...} |
unterzubringen, würde man das sicher nicht so machen, sondern
1 | while(1){"Befehlsfolge"; if(??) break; ...} |
verwenden. Und dann gilt mein Argument wieder: Josef G. schrieb: > Bei einfacher Abbruchbedingung schreibt man > while(??){...} > > Wenn die Ermittlung der Abbruchbedingung > eine lange Befehlsfolge beinhaltet, schreibt man > while(1){Befehlsfolge; if(??) break; ...} > > Man hat also zwei verschiedene Strukturen, > je nach Komplexität der Abbruchbedingung. > > Bei der Schleife mit Hineinsprung hat man beide Male dieselbe > Struktur, nur die Marke *HERE steht unterschiedlich weit > vom bedingen Rückwärtssprung entfernt.
Jörg Wunsch schrieb: > Nur, weil dein Compiler zu blöd ist zum optimieren. Dafür läuft er aber auch auf einem 8bit-Rechner und benötigt nicht die Ressourcen eines PC.
Josef G. schrieb: > Dafür läuft er aber auch auf einem 8bit-Rechner > und benötigt nicht die Ressourcen eines PC. Das ist sicherlich reizvoll. Gewesen. Vor dreißig Jahren. Auch wenn das Programmieren von 8-Bit-Systemen nach wie vor sinn- und reizvoll ist, das auf 8-Bit-Systemen zu tun ist Masochismus. Brauchbare Texteditoren, brauchbare Ein- und Ausgabesysteme, Dateisysteme, die auch Dateinamen verarbeiten können (statt auf wenige Zeichen Länge beschränkte Kürzel) ... all das sind Gründe, die Programmiererei auf einem etwas zeitgenössischeren System zu veranstalten. Ich habe auch noch meinen ersten selbst zusammengelöteten 8-Bit-Rechner herumstehen, der wird demnächst mal abgestaubt und zum 30. seiner Inbetriebnahme auch mal wieder eingeschaltet, eine Zeitlang bewundert und dann wieder ausgeschaltet. Obwohl der unter anderem einen Pascal-Compiler im ROM hat, und so ziemlich den schicksten aller 8-Bit-Prozessoren verwendet (6809).
Rufus Τ. Firefly schrieb: > Josef G. schrieb: >> Dafür läuft er aber auch auf einem 8bit-Rechner >> und benötigt nicht die Ressourcen eines PC. > > Das ist sicherlich reizvoll. Gewesen. Vor dreißig Jahren. Abgesehen davon. Auch vor 30 Jahren gab es schon C-Compiler die problemlos und sauber auf den damaligen 8 Bit Rechnern liefen. Nur weil heutige IDE Megabyteweise Dinge mitbringen, bedeutet das ja nicht, dass das immer so sein muss. Ein C Compiler ist kein besonders umfangreiches Programm. Der gcc hat es natürlich schwerer, weil er so gebaut ist, dass er von den kleinsten bis zu den größten Maschinen mit unterschiedlichen Architekturen klar kommen muss. D.h. alles muss sehr allgemein gehalten sein. > Sogar wenn es ginge, das in den runden Klammern der Struktur > > while(..., ..., ??){...} > unterzubringen, würde man das sicher nicht so machen, sondern > > while(1){"Befehlsfolge"; if(??) break; ...} > verwenden. Und dann gilt mein Argument wieder: Klar. Man kann natürlich immer damit argumentieren, dass die gröbsten Schweinereien in einer Sprache nicht so einfach möglich sind. Die Erfahrung zeigt aber, dass irgendwer immer einen Weg findet :-) Um das klar zu sagen: Wenn du das willst, dann hast du die ganze Entwicklung der strukturierten Programmierung und was man aus den letzten 80 Jahren Programmierung gelernt hat, ganz einfach verschlafen. Der Trend ging nicht grundlos in die Richtung, dass man derartige Schweinereien gezielt abstellen will. Die Praxis zeigt nämlich auch, dass genau diese Schweinereien es nicht sind, mit denen man aus einem Programm Speed herausholt. Ausser natürlich in sinnlosen Demos. Aber in realen Programmen sind diese Dinge nicht der Bottleneck. Ich finde die Schleife mit Hineinsprung durchaus interessant. Aber es ist jetzt nichts, was ich mir unbedingt zu Weihnachten wünschen würde.
:
Bearbeitet durch User
Karl Heinz schrieb: > Auch vor 30 Jahren gab es schon C-Compiler die problemlos und sauber auf > den damaligen 8 Bit Rechnern liefen. Und vor 30 Jahren gab es auch längst 32-Bit 68000 Systeme, die sich weit besser als Host für Compiler eigneten.
Karl Heinz schrieb: > Auch vor 30 Jahren gab es schon C-Compiler die problemlos und sauber auf > den damaligen 8 Bit Rechnern liefen. Insbesondere gab es schon Turbo-Pascal, welches gar nicht mal so schlecht optimiert hat und auch keine derartig verrenkten syntaktischen Konstrukte benötigte, wie Josef sie hier anzetteln will. Josef G. schrieb: > Und dann gilt mein Argument wieder: … für das du bislang immer noch den Anwendungsfall schuldig geblieben bist. Du theoretisierst da um irgendwas herum. Wo zum Geier braucht man derartig kompliziert gestaltete Abbruchbedingungen, die man dann auch noch unbedingt vor dem ersten Abarbeiten der Schleife durchlaufen muss? Alles andere in deinen komplizierten Abläufen kann man ja immer noch ganz normal ans Ende der Schleife setzen:
1 | while (!abbruchbedingung) { |
2 | tuwas(); |
3 | komplizierte_vorbereitung_der_abbruchbedingung; |
4 | }
|
Ich würde mal sagen, in den allermeisten Fällen müsste man selbst bei derzeitigen C-Programmen die Abbruchbedingung noch nichtmal vor dem ersten Schleifendurchlauf testen, weil von der Umgebung her klar ist, dass beim Eintritt in die Schleife auf jeden Fall was zu tun ist. Es würde dann eine do{}while-Schleife genügen.
Jörg Wunsch schrieb: > Insbesondere gab es schon Turbo-Pascal Und das lief in den ersten Inkarnationen sogar auf 8-Bit-Systemen (CP/M).
Rufus Τ. Firefly schrieb: > Und das lief in den ersten Inkarnationen sogar auf 8-Bit-Systemen Und zwar verdammt gut, trotz wenig RAM. Klar, grossartig optimiert hat der Compiler nicht, aber gegenüber den vorher üblichen Compilern, wie UCSD-Pascal mit interpretierem Bytecode, oder den dicken FORTRAN und PL/I Dingern war die Zeit zwischen Quellcodeänderung und Ausführung angenehm kurz. Nur: Muss man das heute auch noch so machen? Schon in der Anfangszeit fand professionelle Entwicklung für 8-Bit Systeme unterhalb der CP/M Klasse auf Minicomputern statt, bevor die PCs deren Rolle übernahmen.
@ A. K. (prx) >Nur: Muss man das heute auch noch so machen? Schon in der Anfangszeit >fand professionelle Entwicklung für 8-Bit Systeme unterhalb der CP/M >Klasse auf Minicomputern statt, bevor die PCs deren Rolle übernahmen. Hat nicht auch Bill Gates so angefangen? Auf dem Rechner der Uni für die "Homecomputer" was programmiert. Da gab dann Ärger.
@Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite >> Und dann gilt mein Argument wieder: >… für das du bislang immer noch den Anwendungsfall schuldig geblieben >bist. Du theoretisierst da um irgendwas herum. Jörg, die "kennst" den Josef doch schon seit Jahren. Immer noch nicht verstanden, wie es um ihn steht? Beitrag "Re: wer kann sich noch an den hex-zeichensatz erinnern?"
Jörg Wunsch schrieb: > für das du bislang immer noch den Anwendungsfall schuldig geblieben Eine Zahl SELECT soll eingelesen werden, wozu mehrere Aktionen nötig sind (Linefeed, Ausgabe von Benutzer-Info und Eingabe-Maske, ...). Hat die Zahl einen Wert 0..3, dann soll dadurch eine von vier Aktionen ausgewählt werden, welche dann auszuführen sind, und es soll eine neue Zahl SELECT eingelesen werden und so fort. Falls aber SELECT den Wert 4 oder größer hat, soll abgebrochen werden. Man hat eine Schleife mit Hineinsprung: Nach dem *HERE stehen die Eingabe der Zahl SELECT und die unterstützenden Aktionen, dann der Rückwärtssprung falls SELECT nicht größer als 3. Vor dem *HERE stehen in einer CASE-Struktur die vier Aktionen, welche durch SELECT ausgewählt werden.
Josef G. schrieb: > Man hat eine Schleife mit Hineinsprung Nein. Hat man nicht. Die Schleife kann auch bei Deiner Anforderung ganz vorne, also an ihrem Anfang begonnen werden. In c-artigem Pseudocode
1 | do |
2 | { |
3 | Select = Hole_Aktion(); |
4 | |
5 | switch (Select) |
6 | { |
7 | case 0 : |
8 | machdies(); |
9 | break; |
10 | |
11 | case 1 : |
12 | machjenes(); |
13 | break; |
14 | |
15 | case 2 : |
16 | machwasanderes(); |
17 | break; |
18 | |
19 | case 3 : |
20 | verbraucherechenzeit(); |
21 | break; |
22 | } |
23 | } |
24 | while (Select < 4); |
Hast du was gegen Funktionen?
1 | do zahl = input(); while (action(zahl)); |
2 | |
3 | int action(int zahl) |
4 | {
|
5 | switch (zahl) { |
6 | case 0: ... |
7 | case 1: ... |
8 | case 2: ... |
9 | case 3: ... |
10 | default: return 0; |
11 | }
|
12 | return 1; |
13 | }
|
:
Bearbeitet durch User
A. K. schrieb: > Hast du was gegen Funktionen? Soweit ist Josef bzw. seine Programmiersprache noch nicht. Aber frag mal in 30 Jahren nach.
Kennen tut er Funktionen jedenfalls, zumindest in C. Ich weiss bloss nicht, was er dagegen hat. Und was ihn insbesondere an Return-Werten stört: Beitrag "Re: Gibt es eine Programmiersprache mit diesem Schleifentyp?" Und so geht natürlich auch:
1 | while ((zahl = input()) < 4) { |
2 | ..
|
3 | }
|
was genau dem C-Klassiker der ersten Stunde entspricht:
1 | while ((c = getchar()) != EOF) { |
2 | ..
|
3 | }
|
OK. Eine Zuweisung mitten in der Bedingung, das gibts wirklich nicht in jeder Sprache.
:
Bearbeitet durch User
A. K. schrieb: > Hast du was gegen Funktionen? Dass ja seine tolle Erfindung dann doch bloß wieder flüssiger als Wasser ist. ;-)
Jörg Wunsch schrieb: > Dass ja seine tolle Erfindung dann doch bloß wieder flüssiger als > Wasser ist. ;-) Nu, das wär doch wirklich was. Suprafluidität - eine völlig reibungslos funktionierende IT.
Josef G. schrieb: > Es gibt in der Tat eine Eins-zu-Eins-Entsprechung zwischen > dem Quelltext und dem erzeugten Code. Und dies ermöglicht > es, durch Addieren von Ausführungszeiten die Dauer von > Programmteilen zu berechnen. Ob es dafür einmal eine > ernsthafte Anwendung geben wird, wird man sehen. Robert L. schrieb: > warum es wichtig sein sollte, dass man (anhand vom Code) abzählen kann, > wieviele takte ein bestimmtes programm jetzt genau braucht, kapier ich > übrigens nicht.. Offenbar gibt es doch Leute, die für so etwas eine Anwendung haben. Beitrag "Funktion mit gleichbleibender Ausführungszeit"
>die für so etwas eine Anwendung haben.
nein, die GLAUBEN für so etwas eine Anwendung zu haben!
dort wurde (wie so oft) gefragt, wie man eine vermeintliche Lösung für
ein Problem umsetzten kann
das PROBLEM ansich aber nicht dargelegt: ob es diese "Lösung" also
überhaupt braucht, oder die beste ist weiß keiner..
Geht auch in "C", sogar ohne explizites "goto": switch (einsprung) while (bedingung) { case EINSPRUNG_1: case EINSPRUNG_2: case EINSPRUNG_3: // Tu' irgendwas mit "bedingung" } Sage niemandem dass Du das von mir hast! Fuer soetwas wird man in jedem Review bei lebendigem Leibe verbrannt ;) Josef G. schrieb: > In einer von mir kreierten Programmiersprache gibt es die > "Schleife mit Hineinsprung" anstelle der while-Schleife. > > Kennt jemand sonst eine Sprache mit dieser Struktur? > > Beitrag "Re: 8bit-Computing mit FPGA"
Helmut S. schrieb: > Sage niemandem dass Du das von mir hast! > Fuer soetwas wird man in jedem Review bei lebendigem Leibe verbrannt ;) Ich sage auch niemandem, daß du es von Duff's Device abgeschaut hast :-) https://en.wikipedia.org/wiki/Duff%27s_device
Klaus W. schrieb: > https://en.wikipedia.org/wiki/Duff%27s_device Habe jetzt den Wikipedia-Artikel doch noch einigermaßen verstanden. Es geht darum, bei Zähl-Schleifen die Zahl der Durchläufe und damit die Zahl der bedingten Rückwärtssprünge, welche Zeit kosten, auf einen Bruchteil zu reduzieren, indem man den Schleifenkern mehrere Male (zB. achtmal) untereinander schreibt und den Startwert des zu dekrementierenden Zählers durch einen Bruchteil (zB. ein Achtel) des ursprünglichen Wertes ersetzt. Falls der ursprüngliche Wert nicht durch acht teilbar ist, springt man zu Beginn in die Schleife zu einer der Kopien des Schleifenkerns. Zwar nicht auf Hochsprachen-Ebene aber bei Assembler-Programmierung wird dieses Problem bei meiner CPU auf andere Weise gelöst. Hier als Beispiel ein Code zur Blockverschiebung (S = Schleifenzähler):
1 | S.RP Setze Schleifenstartadresse auf den GTMX-Befehl |
2 | GTMX Lade (X) in den Akku |
3 | STMY Speichere Akku in (Y) |
4 | IX0 Inkrementiere X |
5 | R.IY Falls S nicht Null ist: |
6 | Dekrementiere S, Inkrementiere Y, Sprung zum Start |
Der kombinierte Befehl R.IY dauert nicht länger als ein einfaches Inkrementieren IY0. Damit entfällt der Anreiz, Schleifendurchläufe zusammenzufassen durch mehrmaliges Hinschreiben des Schleifenkerns. Zu diesem und anderen Aspekten der CPU siehe auch Beitrag "Re: 8bit-Computing mit FPGA"
Mit "mehrmaligem Hinschreiben" war diese Lösung gemeint:
1 | S.RP |
2 | GTMX |
3 | STMY |
4 | IX0 |
5 | IY0 |
6 | GTMX |
7 | STMY |
8 | IX0 |
9 | R.IY |
:
Bearbeitet durch User
Ohne jetzt den wie üblich wirren Thread durchgelesen zu haben, habe ich den Eindruck, dass hier "Repeat [Anweisungen] Until [Bedingung]" wiederentdeckt wurde. Bekannt aus bspw. PASCAL.
Lukas T. schrieb: > Ohne jetzt den wie üblich wirren Thread durchgelesen zu haben, habe > ich den Eindruck, dass hier "Repeat [Anweisungen] Until [Bedingung]" > wiederentdeckt wurde. Bekannt aus bspw. PASCAL. Der Eindruck ist falsch. In dem Thread geht es um die "Schleife mit Hineinsprung", welche while-Schleifen ersetzt und darüber hinaus mehr kann. In meinen letzten zwei Beiträgen geht es um etwas anderes: Bei Zählschleifen, welche bestehen aus dem Schleifenkern und dem Zählvorgang mit bedingtem Rückwärtssprung, verlängert sich die Dauer einer Ausführung des Schleifenkerns um die Dauer des Zählvorgangs mit Rückwärtssprung. Wenn man den Schleifenkern zweimal hinschreibt und die Zahl der Schleifendurchläufe halbiert, ist pro Ausführung des Schleifenkerns nur noch die halbe Dauer eines Zählvorgangs mit Rückwärtssprung anzurechnen, es geht also schneller. Dadurch ergibt sich für den Programmierer ein Konflikt zwischen Geschwindigkeit einerseits und Codelänge/Einfachheit andererseits. Sofern im Schleifenkern ein Adressregister inkrementiert oder dekrementiert wird, wird bei meiner CPU dieser Konflikt vermieden durch kombinierte Befehle, welche ein Adressregister inkrementieren oder dekrementieren und gleichzeitig den Zählvorgang mit bedingtem Rückwärtssprung ausführen, und welche nicht länger dauern als das Inkrementieren/Dekrementieren des Adressregisters allein.
>Dadurch ergibt >sich für den Programmierer ein Konflikt zwischen Geschwindigkeit >einerseits und Codelänge/Einfachheit andererseits. eigentlich nicht: es ist ein "Problem" des Compilers... für den Programmier ist der Code ja der selbe, egal ob der Compiler jetzt auf geschwindigkeit oder größe optimiert.. (es steht auch in dem WIKI artikel zu Duff's Device, dass das aufgrund von modernen comilern nicht mehr/kaum notwendig ist..) ausserdem sind heutige CPU so extrem schnell, dass es in 99,999% der Fälle nicht notwendig ist sich über sowas überhaupt gedaken machen zu müssen und in den restlichen 0.0001% wird man zuerstmal eine schnellere CPU (und vorallem keine 8bit CPU) verwenden.. und DANN erste anfangen mühsam code zu optimieren... (siehe stm32 für ein paar $..) >und welche nicht länger dauern als das >Inkrementieren/Dekrementieren des Adressregisters allein. liegt aber vielleicht daran dass dein "inkremtenieren alleine" zu langsam ist, und nicht dass das kombinierte so schnell ist ;-) ausserdem: wenn du dir anschaust, welche optimierungen "aktuelle" CPU (seit Jahrezehnten) gerade bei bedingten sprüngen machen, ist nicht mal sicher, ob mehrmaliges hinschreiben, schneller ist als normale schleife...
:
Bearbeitet durch User
Robert L. schrieb: > ausserdem: wenn du dir anschaust, welche optimierungen "aktuelle" CPU > (seit Jahrezehnten) gerade bei bedingten sprüngen machen, ist nicht mal > sicher, ob mehrmaliges hinschreiben, schneller ist als normale > schleife... Wer sich dafür interessiert, was in CPUs heute abgeht, der kann man hier reinschnuppern: http://www.realworldtech.com/sandy-bridge/ Leider versteckt David Kanter seine sehr guten Analysen mittlerweile im unbezahlbaren Microprocessor Report.
:
Bearbeitet durch User
In Matlab hätte ich jahrelang diesen Schleifentyp gern gehabt. Ich hatte ein "kleines" Dynamik-FEM-Programm, das ungefähr so aussah:
1 | Preprocessing |
2 | Initialization |
3 | Step-Postprocess |
4 | |
5 | for( ... ) |
6 | Step-Calculation |
7 | Step-Postprocess |
8 | end for |
Das Problem war, daß Matlab nur call by value beherrscht, und da die Übergabe von Matrices > 1GB (sparse) viel zu langsam gewesen wäre, mußten die einzelnen Schritte in der Hauptschleife ausgeschrieben werden, d.h. die beiden Blocks "Step-Prostprocess" waren 1:1-Textkopien des Quelltextes. Sehr lästig. Da hätte man den Einsprung in eine Schleife gut gebrauchen können. Das ist aber eher das gegenteilige Extrem, was Programmgröße und Programmiersprache (Hochsprache) angeht.
:
Bearbeitet durch User
Robert L. schrieb: > wenn du dir anschaust, welche optimierungen "aktuelle" CPU > (seit Jahrezehnten) gerade bei bedingten sprüngen machen Zudem werden auch die Compiler immer schlauer und nutzen diese Features der aktuellen Prozessoren meist besser als man dies durch explizite Programmierung (sei es in Assembler oder durch neue Sprachkonstrukte wie der "Josef-G.-Schleife") kann. Schon vor ca. 20 Jahren war der C-Compiler für den TMS320C30 (DSP von TI) in der Lage, eine Schleife komplett umzustellen, um damit Features des Prozessors wie bspw. die Multiply/Accumulate (parallele Addition/ Multiplikation) und Single-Instruction-Repeat (Wiederholung einer Einzeinstruktion ohne Schleifen-Overhead) nutzen zu können. Beispiel Skalarprodukt: So schreibt man den Code üblicherweise hin:
1 | double dot(double x[], double y[], int n) { |
2 | double sum; |
3 | int i; |
4 | |
5 | sum = 0.0; |
6 | for(i=0; i<n; i++) |
7 | sum += x[i] * y[i]; |
8 | |
9 | return sum; |
10 | }
|
Er ist zwar leicht schreib- und lesbar, hat aber den Nachteil, dass die Addition in der Schleife vom Ergebnis der Multiplikation abhängt, weswegen diese beiden Operationen nicht parallel ausgeführt werden können. Der Compiler erzeugt daraus Code, der äquivalent zu diesem ist:
1 | double dotopt(double x[], double y[], int n) { |
2 | double sum, prod; |
3 | int i; |
4 | |
5 | sum = 0.0; |
6 | prod = x[0] * y[0]; |
7 | for(i=1; i<n; i++) { |
8 | sum += prod; |
9 | prod = x[i] * y[i]; |
10 | }
|
11 | sum += prod; |
12 | |
13 | return sum; |
14 | }
|
Durch die Umstellung der Operationen innerhalb der Schleife können sie jetzt parallel ausgeführt werden. Die erste Multiplikation und die letzte Addition der gesamten Berechnung werden außerhalb der Schleife ausgeführt, weswegen die Schleife statt n nur n-1 Durchläufe hat. Das alles bastelt der Compiler ohne Zutun des Programmierers so hin mit dem Ergebnis, dass die Berechnung statt 5n+1 nur n+2 Maschinenzyklen dauert. Das ist eine Geschwindigkeitssteigerung um den Faktor 5, ohne dass dafür neuartige Schleifenkonstrukte vonnöten wären. Und die Compiler sind seither nicht schlechter, sonden noch viel besser geworden Josef G. schrieb: > Wenn man den Schleifenkern zweimal hinschreibt und die Zahl der > Schleifendurchläufe halbiert, ist pro Ausführung des Schleifenkerns > nur noch die halbe Dauer eines Zählvorgangs mit Rückwärtssprung > anzurechnen, es geht also schneller. Loop-Unrolling wäre im obigen Beispiel sogar kontraproduktiv, da dadurch ein Schleifen-Overhead entstünde, der vorher nicht vorhanden war. Und auf Prozessoren, wo es tatsächlich einen Nutzen bringt, überlässt man es sinnvollerweise meist ebenfalls dem Compiler, so dass man damit nicht den Quellcode verunstalten muss. Walter T. schrieb: > Preprocessing > Initialization > Step-Postprocess > > for( ... ) > Step-Calculation > Step-Postprocess > end for > > Das Problem war, daß Matlab nur call by value beherrscht, und da die > Übergabe von Matrices > 1GB (sparse) viel zu langsam gewesen wäre, > mußten die einzelnen Schritte in der Hauptschleife ausgeschrieben > werden, d.h. die beiden Blocks "Step-Prostprocess" waren 1:1-Textkopien > des Quelltextes. Sehr lästig. Matlab hat doch eine Break-Anweisung.Warum also nicht so:
1 | Preprocessing |
2 | Initialization |
3 | |
4 | forever |
5 | Step-Postprocess |
6 | if <exit condition> then |
7 | break |
8 | end if |
9 | Step-Calculation |
10 | end for |
A. K. schrieb: > Leider versteckt David Kanter seine sehr guten Analysen mittlerweile im > unbezahlbaren Microprocessor Report. Der wird doch nicht etwa hier bei Joseph abschreiben?
Yalu X. schrieb: > Matlab hat doch eine Break-Anweisung.Warum also nicht so: Die klassische Antwort: "Ja, aber." Matlab war damals (ich mache diesen Kram leider nicht mehr) extrem viel schneller, wenn es vorher die Anzahl der Schleifendurchläufe kannte. Sprich: Eine einfache for-Schleife mit konstantem Initialsierungsvektor war deutlich schneller als eine while-Schleife oder wenn in der Schleife "break" oder "continue"-Anweisungen vorhanden waren. Einen Tod muß man halt immer sterben.
Walter T. schrieb: > Yalu X. schrieb: >> Matlab hat doch eine Break-Anweisung.Warum also nicht so: > > Die klassische Antwort: "Ja, aber." Matlab war damals (ich mache diesen > Kram leider nicht mehr) extrem viel schneller, wenn es vorher die Anzahl > der Schleifendurchläufe kannte. Macht sich der Schleifen-Overhead auch dann noch bemerkbar, wenn innerhalb der Schleife Walter T. schrieb: > Matrices > 1GB verarbeitet werden? So lahm wird doch selbst der Matlab-Intepreter nkaum sein, oder?
Yalu X. schrieb: > Macht sich der Schleifen-Overhead auch dann noch bemerkbar, wenn > innerhalb der Schleife > > Walter T. schrieb: >> Matrices > 1GB > > verarbeitet werden? So lahm wird doch selbst der Matlab-Intepreter nkaum > sein, oder? So lahm ist der Interpreter gar nicht. Im Vergleich zu Fortran-Programmen von mittelmäßig darin begabten Ingenieuren (da schließe ich mich ein) ist er sogar ziemlich fix. Ich nehme an, daß er einfach deutlich besser parallelisieren kann, wenn er schon vorher die Anzahl der Schleifenläufe kennt.
:
Bearbeitet durch User
Oh mein Gott. Und ich dachte wir hätten den Schmarrn endlich überstanden.
Robert L. schrieb: >>und welche nicht länger dauern als das >>Inkrementieren/Dekrementieren des Adressregisters allein. > > liegt aber vielleicht daran dass dein "inkremtenieren alleine" zu > langsam ist, und nicht dass das kombinierte so schnell ist ;-) Bei meiner CPU dauern alle Operationen 1 oder 2 oder 3 "Vollzyklen", wobei ein Vollzyklus aus den Halbzyklen tA und tB besteht. Das Inkrementieren/Dekrementieren eines Adressregisters dauert 1 Vollzyklus, ebenso die kombinierten Befehle. Beim Inkrementieren eines Adressregisters wird bei tA das Adressregister in ein Hilfsregister übertragen, an dessen Ausgang ein Inkrementiernetz hängt. Bei tB wird der Ausgang des Inkrementiernetzes in das Adressregister geschrieben. Gleichzeitig wird bei tA der Programmzähler P auf dem Adressbus ausgegeben und der nächste OpCode in ein Eingangs-Pufferregister geladen. Bei tB wird er dann in das für das folgende tA zuständige OpCode-Register übertragen. Ebenfalls gleichzeitig wird bei tA der Programmzähler P in ein Hilfsregister übertragen, an dessen Ausgang ein Inkrementiernetz hängt, bei tB wird das Ergebnis in den Programmzähler geschrieben. Bei den kombinierten Befehlen wird, sofern der Schleifenzähler nicht Null ist, bei tA statt P die Schleifenstartadresse R auf dem Adressbus ausgegeben zum Einlesen des nächsten OpCodes, und in das Hilfsregister, an dessen Ausgang das Inkrementiernetz hängt, wird ebenfalls R statt P übertragen. Das Ergebnis kommt wieder nach P. Bei den kombinierten Befehlen wird gleichzeitig bei tA der Schleifenzähler S in ein Hilfsregister übertragen, an dessen Ausgang ein Dekrementiernetz hängt. Bei tB wird das Ergebnis in S gespeichert, falls S vorher nicht Null war. Man sieht, dass wirklich alles parallel ausgeführt wird und nicht länger dauert als das Inkrementieren des Adressregisters allein. Das Inkrementieren des Adressregisters wird auch nicht künstlich verlängert, um gleiche Dauer wie bei den kombinierten Befehlen zu erreichen. Würde man das CPU-Konzept grundlegend ändern und die Hilfsregister einsparen und statt tA/tB nur eine Phase haben, könnte man das auf alle genannten parallel ablaufenden Vorgänge anwenden, und die gleiche Dauer von kombinierten Befehlen und einfachem Inkrementieren würde weiter gelten.
Josef G. schrieb: > Das Inkrementieren/Dekrementieren eines Adressregisters > dauert 1 Vollzyklus, ebenso die kombinierten Befehle. nein... wenn ich das jetzt richtig verstanden habe, ist dein "kominierter Befehl" kein echter "bedingter Sprung" .. der würde nämlich eine Zieladresse beinhalten.. (im nahen und sehr nahen Umfeld) http://www.mathemainzel.info/files/x86asmref.html#jne du hingegen hast hier Arbeit auf 2 Befehle aufgeteilt (S.RP und R.IY) d.h. man muss fairerweise auch die Zeit von beiden addieren.. ist mir aber soweiso unklar, wie man hiermit verschachtelte schleifen programmierne kann .. p.s. schön dass du alle anderen Ausführungen ignoriert hast und nur auf das Antwortest was dir lieb ist..
Robert L. schrieb: > du hingegen hast hier Arbeit auf 2 Befehle aufgeteilt (S.RP und R.IY) > d.h. man muss fairerweise auch die Zeit von beiden addieren.. Nein, man muss die Zeiten nicht addieren. Das S.RP steht ausserhalb der Schleife und wird nur 1-mal ausgeführt. > ist mir aber soweiso unklar, wie man hiermit verschachtelte schleifen > programmierne kann .. Das ist in der Tat nicht möglich. Der schnelle Rückwärtssprung ist nur für die innerste Schleife gedacht, für äussere Schleifen muss man bedingte Rückwärtssprünge ( B.xx nn ) mit expliziter Angabe der Sprungdistanz nn verwenden. Bei dieser äusseren Schleife ist aber die Dauer des Rückwärtssprungs vernachlässigbar gegenüber der Gesamtdauer der inneren Schleife.
Die schnellen Rückwärtssprünge bei meiner CPU gibt es natürlich auch ohne Inkrementieren oder Dekrementieren eines Adressregisters.
Angeregt durch diesen aktuellen Thread Beitrag "goto verpönt - was dann nehmen?" möchte ich hiermit nochmal einen Versuch machen, für meine Programmiersprache zu werben. Es gibt in dieser Sprache kein goto, kein break, kein continue, und in jedem Programm oder Unterprogramm genau 1 return, nämlich am Ende des Programms oder Unterprogramms. Die Strukturelemente sind folgende:
1 | Bedingte Anweisung: IF.c ... ENDF |
2 | Verzweigung: IF.c ... ELSE ... ENDE |
3 | Case-Struktur: CASE /nn ... NEXT /nn ... LAST |
4 | Do-while-Schleife: LOOP ... RP.c |
5 | While-Schleife: L.JP ... HERE ... RP.c |
c steht hier für die Sprungbedingung, nn steht für eine Zahl fallend bis 00, NEXT /nn ist entsprechend mehrmals anzugeben. RP.c steht für wiederhole falls c. Ebenso wie IF.c ... ELSE ... ENDE zählt die Schleife mit Hineinsprung L.JP ... HERE ... RP.c als ein Block mit zwei Unterblöcken, die strenge Blockstruktur wird durch den Hineinsprung nicht durchbrochen. Unterprogramme: Es gibt keine functions mit Rückgabewert, sondern nur einfache Unterprogramme, bei welchen alle Parameter gleichberechtigt sind. Es werden ausschließlich Zeiger übergeben. http://www.mikrocontroller.net/articles/8bit-Computer:_bo8h
Josef G. schrieb: > Angeregt durch diesen aktuellen Thread > Beitrag "goto verpönnt - was dann nehmen?" > möchte ich hiermit nochmal einen Versuch machen, > für meine Programmiersprache zu werben. Du willst doch nicht etwa Kurt die Schau stehlen?
Josef G. schrieb: > Unterprogramme: Es gibt keine functions mit Rückgabewert, > sondern nur einfache Unterprogramme, bei welchen alle Parameter > gleichberechtigt sind. Reiner Masochismus bringt mich dazu, Dich zu fragen, was das sein soll.
Rufus Τ. F. schrieb: > was das sein soll. Beim Aufrufen werden nach dem Namen des Unterprogramms die Namen der Parameter aufgelistet. Das Unterprogramm kann die Parameter lesen und beschreiben, sofern der betreffende Variablentyp das zu- lässt. Damit kann jeder Parameter auch für die Rückgabe verwendet werden. Einen speziellen Rückgabe-Parameter gibt es nicht.
Rufus Τ. F. schrieb: > Reiner Masochismus bringt mich dazu, Dich zu fragen, was das sein soll. Vereinfacht den Compiler. Sowohl in der so sehr einfachen Syntax, als auch bei der Umsetzung in Code. Verschachtelte Aufrufe kann es damit nicht geben, so dass man sich keine andere Ausrede überlegen muss, weshalb man sie nicht zulässt. Und dass sein Sinn für Ästhetik deutlich anders entwickelt ist, als bei jedem anderen, wissen wir bereits.
:
Bearbeitet durch User
Rufus Τ. F. schrieb: > Reiner Masochismus Rainer Unsinn ist hier bekannt, aber wer ist Reiner Masochismus?
A. K. schrieb: > Verschachtelte Aufrufe kann es damit nicht geben, Bin zwar nicht ganz sicher, was damit gemeint ist, aber, nur damit es keine Missverständnisse gibt: Unterprogramme können selbstverständlich ihrerseits andere Unterprogramme aufrufen.
:
Bearbeitet durch User
Josef G. schrieb: > Bin zwar nicht ganz sicher, was damit gemeint ist, Ja, das war etwas undeutlich ausgedrückt. In diesem Fall meine ich einen Funktionsaufruf als Argument eines Unterprogrammaufrufs. Also sowas wie call foobar (1, sin(0.5)) statt temp = sin(0.5) call foobar (1, temp) oder bei dir sinngemäss wohl call sin (0,5, temp) call foobar (1, temp) falls du Konstanten als Argumente überhaupt zulässt.
:
Bearbeitet durch User
Josef G. schrieb: > Beim Aufrufen werden nach dem Namen des Unterprogramms die Namen > der Parameter aufgelistet. Das Unterprogramm kann die Parameter > lesen und beschreiben, sofern der betreffende Variablentyp das zu- > lässt. Toll. Und was ist daran jetzt besonders, abgesehen davon, daß man beim Betrachten des Aufrufs eines "Unterprogrammes" nicht sieht, welche Parameter verändert werden können (ohne sich das "Unterprogramm" näher anzusehen)? Praktisch jede andere Programmiersprache kennt das auch - in Pascal nennt sich das "call by reference", in C verwendet man Pointer auf die Argumente, und in C++ kann man das auch machen, oder die dafür vorgesehenen Referenzen verwenden. Bei C/C++ und der Verwendung von Pointern kann man auch ohne Betrachtung der aufgerufenen Funktion (bzw. ihres Prototypen) erkennen, welche der Parameter verändert werden könnten und welche nicht - wo kein Pointer übergeben wird, kann auch nichts verändert werden. Oh, und man hat in all diesen Programmiersprachen auch die Möglichkeit, einen expliziten Rückgabewert zu verwenden, was z.B. zum Zurückgeben von Fehlercodes sinnvoll sein kann. Bei der "ich schreib' hier mal zehn Parameter hin"-Methode muss man jedes mal erneut vereinbaren, welcher davon ein etwaiger Fehlercode sein könnte. Nein, ich vermag keinen einzigen Vorteil zu erkennen.
Rufus Τ. F. schrieb: > Praktisch jede andere Programmiersprache kennt das auch Wie etwa FORTRAN, wo stets call by reference verwendet wird.
Rufus Τ. F. schrieb: > Nein, ich vermag keinen einzigen Vorteil zu erkennen. Das ist der Vorteil der Methode!
Es ist oft genug grad andersrum, d.h. es gibt einen gewisse Neigung, mehrere Returnobjekte zuzulassen, nicht nur eines. Entweder, indem man Klassenobjekte als Returnwert verwendet (wie in C++), oder indem die Sprache ganz offizell mehrere Returnobjekte zulässt (wie etwa Go). Ein klassisches Beispiel dafür ist eine Funktion, die neben dem eigentlichen Returnwert auch noch mitteilen muss, ob sie überhaupt einen gültigen Wert produzieren konnte. Damit entsteht ein Paar aus dem Wert selbst und einem Error-Flag. Natürlich kann man das auch per Referenz-Parameter implementieren. Leider bewegt man sich damit allzu leicht von übersichtlicherer funktionaler Programmierung mit seiteneffektfreien Funktionen weg und die Programme werden zudem auch länger. Hinzu kommt, dass Objekte in Registern weit besser aufgehoben sind als im Speicher, was die Rechenleistung angeht. Übergabe von Werten in Registern ist daher sehr viel effizienter als die Übergabe von Werten im Speicher und deren Speicheradressen in Registern. Das gilt gleichermassen für Parameter wie für Returnobjekte. Es gibt Programmiersprachen, die Parameter in "in", "out" und "inout" unterscheiden. Bei "in" und "out" kommt man dem Prinzip nach ganz gut ohne Speicheradresse aus und "out" Parameter liessen sich auch als Returnobjekte darstellen. Ob Grunde bewegt man sich mit dem konsequenten Verzicht auf call by value also zurück in 50er Jahre, die Zeit der Entstehung von FORTRAN. Eine Zeit, in der Register sehr aufwändig und daher selten waren.
:
Bearbeitet durch User
>möchte ich hiermit nochmal einen Versuch machen, >für meine Programmiersprache zu werben. die nur auf einer 8-bit CPU funktioniert, die es nicht mal gibt.. na viel glück..
Rufus Τ. F. schrieb: > Nein, ich vermag keinen einzigen Vorteil zu erkennen. Die Anwendung des Prinzips Minimalismus hat immerhin den Vorteil, dass die Sprache leichter zu erlernen wäre, wenn es denn jemand versuchen würde.
Da ist nichts minimalistisches. Eine Programmiersprache wie C ist minimalistisch, das ist auch einer der Gründe für ihren langanhaltenden Erfolg.
Rufus Τ. F. schrieb: > Da ist nichts minimalistisches. > Eine Programmiersprache wie C ist minimalistisch, Also diese Antwort versteh ich nun wirklich nicht. Ausgangspunkt waren meine Aussagen: Josef G. schrieb: > Unterprogramme: Es gibt keine functions mit Rückgabewert, > Es werden ausschließlich Zeiger übergeben. In meiner Sprache gibt es also nur eine Sorte von Parametern. In C gibt es mehrere Sorten. Da ist es doch offensichtlich, dass in dieser Hinsicht meine Sprache minimalistischer ist als C.
nur weil was minimalistisch ist, ist es nicht automatisch einfacher.. Schach Regeln sind sehr einfach.. ist deshalb Schach spielen einfach? gerade in/out/var/const parameter machen vieles einfacher gäbs das nicht, müsste man es eben in die doku schreiben.. (nur lesen leider die meisten Compiler die doku nicht, und können somit auch nichts überprüfen..)
Josef G. schrieb: > Da ist es doch offensichtlich, dass > in dieser Hinsicht meine Sprache minimalistischer ist als C. Auf den Nutzen bezogen hast du wohl recht.
Josef, wenn du damit vor 50 Jahren gekommen wärst, hättest du evtl. Interesse erregen können. Heute wirst du damit höchstens belächelt...
Hau noch mindestens 10 Jahre drauf. Vor 50 Jahren gab es FORTRAN, Lisp, Algol, APL, COBOL, BASIC, PL/I, BCPL(*), ... Alle lesbarer. ;-) *: aka "Before C Programming Language". Für B langts noch nicht ganz.
:
Bearbeitet durch User
Der Fairness halber sollte man ASM als Vergleich heranziehen - damit wurde seinerzeit noch sehr viel gemacht. Das alles ändert aber nichts an der Lächerlichkeit, ein Projekt wie dieses hier heutzutage als Ultima Ratio anzupreisen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.