Kann man sich eigene Statement´s programmieren?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
BlackJack

Dammit… *Silizium* natürlich. Ich hatte mir schon gedacht, dass ich das, was ich gestern Nacht so gegen 2 Uhr zusammengeschrieben habe, besser liegen lasse und morgens noch einmal überfliege, bevor ich es abschicke. Und dann sowas… :oops:
lunar

Bleibt nur noch die Frage, an was du nachts um zwei gedacht hast, wenn dieses Wort den Weg in dein Posting gefunden hat ;) Ich glaube jetzt einfach mal, dass es nur eine fehlgeleitete Übersetzung des englischen Silicone war ;)
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

@str1442: Ich verstehe Dich immer noch nicht so richtig. Was willst Du mit Sätzen wie "Der Interpreter sieht das ganze nun so, wie es ist. […] Er sieht es absolut." sagen? Zumal der Interpreter den Code da nicht sieht, sondern irgend welchen Bytecode, der von der Sprachdefinition noch nicht einmal festgelegt wird.
Für den Interpreter ist der Quellcode "Data". Beziehungsweise das daraus erstellte $COMPILAT.

Der Interpreter hat intern Funktionen und Tätigkeiten, und arbeitet mit den kompletten Daten. In seiner Funktion als Interpreter führt er natürlich bei Statements intern diverse Tätigkeiten durch.
Und mindestens die ``def``-Zeile ist Teil des ausgeführten Programms weil ``def`` in Python eine ausführbare Anweisung (Statement) ist und keine Deklaration. Die Anweisung bindet den Namen `f` an ein Funktionsobjekt, dass den Code repräsentiert. Der wird zwar erst beim Aufruf ausgeführt, ist aber sehr wohl Teil des Programms und keineswegs irrelevant. Wenn das nicht passiert könnte man den Code später nicht ausführen.
Natürlich. Aber das eigentliche Programm ist ja die Logik. Diese wiederrum nutzt Funktionen (oder Klassen, oder ...), um weitere Logik zu implementieren. Schlussendlich kommt man bei Python Standard Funktionen und Ausdrücken an, die wiederrum selbst eine interne Logik haben und sich eventuell an $INTERPRETER_SPRACHE-Libs bedienen.

Das ist erstmal das Programm. Jede Funktion ist nun eine Subroutine, die in sich abgeschlossen ist (meistens, aber global Zugriffe usw bilden hier keine Ausnahme; moment.). Der Interpreter wiederrum verbindet nun jedes kleine Unterprogramm und macht die Funktionen verfügbar. Eben durch Statements, wie zb def. Def an sich gehört aber nicht zum Programm: Es steht natürlich in der src, aber es greift auf eine Interpreterfunktion zu. Der Interpreter verbindet so alles miteinander.

Würde ich ohne Funktionen arbeiten wollen, könnte ich die Subroutine auch (stark zersplittert zwar) einfach so ins Modul schreiben. Die *Logik* bleibt mehr oder weniger die gleiche, außer, das man sich eine Heidenarbeit machen würde.

Eine Funktion hat also unmittelbar etwas mit Logik innerhalb eines Programmes zu tun, ein Statement dagegen ist essenziell für den Interpreter.

Genauso bei "class": class greift intern auf Interpreter Features zurück, dieser aber ruft type auf, was die *Logik* einer Klasse implementiert. Würde man nun direkt mit type() Klassen erstellen, wäre die Logik von Klassen unmittelbar ins Programm verstrickt. Klassen sich auch nur ein Feature Objekt-Orientierter Sprachen. Und ich kann ein Hello World Programm sowohl in Java als auch in C schreiben, mit syntaktischen Unterschieden, aber mit dem (mehr oder weniger) gleichem Ergebnis.

Bei while und co wird auch nur auf ein Interpreter Feature zurückgegriffen, die eigentliche Logik des Programmes könnte man vermutlich auch anders implementieren. Deswegen ist
Assembler
auch kein schlechtes Beispiel, da ich ja genau das aussagen wollte: In Assembler gibt es nichtmal Funktionen. Trotzdem *könnte* man komplexere Programme schreiben. Eben weil man Logik in ein Programm gießt, nicht irgendwelche Sprach Features.
aber spätestens wenn die Obergrenze der Schleife nicht fest steht, kannst Du sie nicht so einfach durch mehrmaliges hinschreiben entfernen weil Du ja nicht weisst wie oft Du wiederholen musst. Und ein ``if`` kannst Du schon mal gar nicht von der Programmlogik trennen.
Ich könnte aber auch BASIC::goto (bofh!!1) verwenden. Das Ergebnis wäre von der Logik her das gleiche. Ich greife auf ein anderes Sprach Feature zurück, und vermeide so if und while / for. goto gibts in Python ja auch nicht. Trotzdem kann ich zu BASIC Programmen logisch aquivalenten Code schreiben.

Der entscheidende Unterschied, den ich zu Funktionen (als eigenständige Subroutinen, die mitteln Sprach Features mit dem Rest der Logik verbunden werden bzw erst lauffähig gemacht werden (return)) mache, ist der, das Funktionen *Programm*logik enthalten sollten. *Nicht* Sprach-Logik. Diese Funktionen greifen wiederrum auf andere Funktionen zu (die mittels der Sprache integriert werden), die aber selbst wieder nur Programmlogik festlegen.

Deswegen ist eine Funktion für das Programm eine Logiktätigkeit. Statements sind Sprachtätigkeiten.
Die Anweisungen sind wesentlicher Bestandteil der Syntax dieser Sprachen.
Ja. Der Sprachen.
Ich sehe auch keine "Belästigung" mit Sprachinterna, wenn ich statt ``raise Exception("blah)"`` in Io ``Exception raise("blah")`` schreiben muss. Man könnte ja genau so gut sagen dass einen Python damit "belästigt", das ``raise`` als Anweisung geschrieben werden muss.
Es gibt viele Wege, etwas zu tun. Aber Dinge sollten konsequent getan werden. In Python ist man sehr dynamisch unterwegs (und hat damit ziemliche Vollmachten), aber Python hält Dinge recht einfach und transparent. Natürlich könnte man solche Exception Methoden nutzen. Dagegen ist ja auch nichts einzuwenden. Im Gegenteil, wenn eine Sprache einem soetwas konsequent bereitstellt und man direkt auf Sprachinternas zugreifen sollte, warum nicht? Aber Python will in erster Linie ja eine transparente, einfache Sprache bleiben, die einem trotzdem nichts versperrt, oder irr ich? Insofern ist der Weg über Statements als Abstraktion zu Sprachfeatures doch absolut ok. Ich würde eine Sprache wie Io und deren Umsetzungen natürlich nicht ablehnen; nur in vielen Sprachen ist der Weg über Statements als Trennung zwischen Programm und Sprachlogik eine gute Lösung. (Io sieht übringens nett aus)

@Leonidas:

Gut, man muss es nicht wissen, aber ich persönlich würde, wäre mir das neu, sehr gerne erfahren, warum das immer type() sein muss und ob ich da nicht noch was anderes nutzen kann, etc usw ;) Man wird eben automatisch tiefer in Sprach Internas
"reingezogen".
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

str1442 hat geschrieben:Genauso bei "class": class greift intern auf Interpreter Features zurück, dieser aber ruft type auf, was die *Logik* einer Klasse implementiert. Würde man nun direkt mit type() Klassen erstellen, wäre die Logik von Klassen unmittelbar ins Programm verstrickt. Klassen sich auch nur ein Feature Objekt-Orientierter Sprachen.
Nein. Wie du selbst sagst, braucht es keinen syntaktischen Zucker, und somit kann man in C genauso objektorientiert programmieren wie irgendwo anders. Scheme selbst ist gar nicht objektorientiert, hat aber 7 verschiedene Objektsysteme, Common Lisp hat hingegen eines, dass von den Möglichkeiten die es bietet seinesgleichen sucht. Aber das ist alles nur Syntax, semantisch lässt sich das wie in C mit Structs und Funktionen nachbauen.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Nein. Wie du selbst sagst, braucht es keinen syntaktischen Zucker, und somit kann man in C genauso objektorientiert programmieren wie irgendwo anders. Scheme selbst ist gar nicht objektorientiert, hat aber 7 verschiedene Objektsysteme, Common Lisp hat hingegen eines, dass von den Möglichkeiten die es bietet seinesgleichen sucht. Aber das ist alles nur Syntax, semantisch lässt sich das wie in C mit Structs und Funktionen nachbauen.
Das man das kann, ist mir natürlich klar. Mir ging es aber um die Unterstützung (zb durch syntactic sugar) direkt durch Sprachen.

Im Vergleich mit Basic: Ich weiß zwar nicht, ob es in BASIC Funktionen gab (konnte auf die Schnelle nichts ähnliches finden), aber die könnte man mittels goto und einer globalen Variablen als return Variable auch emulieren.

Mir ging es mehr um den Vergleich beim "Hello Word" mit Java, da man hier ja gezwungen ist, pseudoobjectorientiert zu arbeiten :D
BlackJack

@str1442: Man kann nicht alle Python-Anweisungen durch "statisches Ersetzen" beseitigen. ``for``/``while``-Schleifen deren Wiederholungen nicht statisch ermittelt werden können und ``if``/``else``, sowie Ausnahmebehandlung müssen mindestens erhalten bleiben, sonst kann man keine brauchbaren Programme schreiben. Die kann man letztendlich unter dem Begriff "bedingte Sprunganweisungen" zusammenfassen. Und das ist *Programmlogik* und nicht irgend etwas was nichts mit dem Programm sondern nur der Sprache zu tun hat.

Bei Assembler hängst Du mich komplett ab. Versuch mal ein Assemblerprogramm ohne Anweisungen zu schreiben. Das geht gar nicht, also kann man sie nicht von der Programmlogik trennen.

Und ich für meinen Teil giesse meine Programme auch in Sprach-Features. Was mir eine Sprache an Werkzeugen bietet, beeinflusst deutlich *wie* ich die Logik strukturiere und umsetze.

Mit `goto` alleine kannst Du kein `if` und keine Schleifen-Anweisungen vermeiden, Du brauchst mindestens eine bedingte Sprunganweisung. Ich weiss aber auch nicht was es jetzt bringt eine Anweisung durch eine andere zu ersetzen. Denn `goto` ist in BASIC eine Anweisung. In Python kann man `goto` übrigens mit Ausnahmen nachbauen (http://entrian.com/goto/) und es sieht fast aus wie eine Anweisung. :-)

Deine Aufteilung in Python von Funktionen -> Programmlogik und Anweisung -> Sprachtätigkeiten die mit der Programmlogik nichts zu tun haben, hält bei bedingten Sprüngen nicht Stand. Auf der anderen Seite kann man die Polymorphie bei Methodenaufrufen auch als bedingte Sprünge ansehen ohne das eine Anweisung verwendet wird.

Io halte ich vom Sprachentwurf konsequenter als Python, weil er wesentlich einfacher ist. Die Syntax ist viel simpler gestrickt. Man hat Objekte, kann denen Nachrichten schicken und bekommt Objekte als Anworten, denen man wieder Nachrichten schicken kann usw. (Die API bei den Objekten in der Io-Standardbibliothek ist allerdings IMHO teilweise nicht so besonders gelungen.)

Wo fangen bei Dir Sprachinterna an und was ist direkter Zugriff darauf? Ob ich nun eine ``raise``-Anweisung verwende oder eine `raise`-Methode ist doch egal, ich habe in beiden Fällen nichts mit der Implementierung zu tun, die ein Sprachentwickler geschrieben hat. Und wenn einem eine Sprache ein Objekt in einer Methode zur Verfügung stellt, was den Aufruf selbst darstellt und zum Beispiel den Sender und den Empfänger kennt, ist das kein Sprachinterna mehr, sondern ganz normale API. Zum Beispiel habe ich nicht das Gefühl, dass ich im Stack-Beispiel tief in der Sprache rumwühle, wenn ich in `Stack with` nicht `Stack` klone, sondern den Empfänger der Nachricht. Das ist ja sozusagen die Klasse und in Python hätte ich an der Stelle auch eine `classmethod()` geschrieben.

Python versperrt einem gewisse Dinge durchaus. Guido wollte keinen ternären Operator in der Sprache haben, hat dann aber schliesslich aufgegeben, weil er gesehen hat, was sich die Leute für hässliche Alternativen basteln. Oder die ``with``-Anweisung: Wurde eingeführt, weil kaum jemand konsequent saubere Datei-Behandlung geschrieben hat, weil das Äquivalent zu ``with`` "Boilerplate"-Code ist, den man nicht so einfach selbst in einer Funktion verschwinden lassen kann. Beides könnte man sich in Io, Lisp, oder Scheme selber schreiben.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

@str1442: Man kann nicht alle Python-Anweisungen durch "statisches Ersetzen" beseitigen. ``for``/``while``-Schleifen deren Wiederholungen nicht statisch ermittelt werden können und ``if``/``else``, sowie Ausnahmebehandlung müssen mindestens erhalten bleiben, sonst kann man keine brauchbaren Programme schreiben. Die kann man letztendlich unter dem Begriff "bedingte Sprunganweisungen" zusammenfassen. Und das ist *Programmlogik* und nicht irgend etwas was nichts mit dem Programm sondern nur der Sprache zu tun hat.
Bei Assembler hängst Du mich komplett ab. Versuch mal ein Assemblerprogramm ohne Anweisungen zu schreiben. Das geht gar nicht, also kann man sie nicht von der Programmlogik trennen.
Das hab ich ja auch nie gesagt. Ich habe Assembler nur als Beispiel herangezogen, weil man dort eben außer einfachen Anweisungen gar nichts machen kann. Zumindest manuell. Sollte illustrieren, das Funktionen genauso wie Klassen nicht since creation vorhanden waren. Damit wollte ich klar machen, das eine Funktion ein eigenes Unterprogramm ist, und es sehr wohl von der Sprache abhängt, ob man überhaupt eine Funktion nutzen kann. Ohne Anweisungen (und damit Assembler-"Sprachfunktionen"; ich sag gleich was dazu) keine Funktionen, was ist also bei so einer Sprache die Funktion und wie lässt sie sich von einer "Anweisung" (was auch immer Sprache xyz sich genau dadrunter vorstellt) unterscheiden?

Zu Assembler selber: Wie du selbst sagst, greift Assembler direkt auf die "Hardwareebene" zu. Im Grunde ist Assembler eine Art Wrapper für die Hardwarefunktionen. Alle Anweisungen in Assembler greifen also direkt auf die Hardware und deren Funktionen zu, und abstrahieren somit mittels Anweisungen etwas von der Hardware.

Man stelle sich diesen Ansatz ein wenig erweitert vor: Angenommen, die Hardware könnte If Anweisungen sowie while / for Anweisungen ausführen. Mittels Anweisungen verschafft man sich einen Zugriff dadrauf. Genauso verstünde die Hardware das Konzept der Funktion.

Jetzt würde man Anweisungen für Hardwaretätigkeiten nutzen. Logik selber aber wären Funktionen, die mittels Anweisungen ja wieder implementiert werden. Diese Funktionen werden wiederrum in anderen Funktionen genutzt usw.

Würde man nun versuchen, zum Zweck der Generalisierung, diese Anweisungen selber in Funktionen zu legen, würde man intern ein Feature der Hardware (nämlich Funktionen) benutzen, um auf ein anderes zuzugreifen. Verletzt das nicht ziemlich stark die Abstraktion? Grade bei so generellen Dingen wie if, for, while?

Wenn Funktionen also in jeder Sprache (die das Funktionskonzept beherrscht) aus anderen Funktionen bestehen, bis man schließlich auf der niedrigsten Ebene irgendwenn ankommt (oder, wie Python es tut, andere Sprachen nutzt), so sind diese Funktion pure Logik *wenn* die genutzten Anweisungen (als Sprach "Features") nicht selber durch diese Logik dargestellt werden (als Funktionen). Wenn das der Fall ist, mag das nicht sonderlich tragisch sein, aber dann sind Sprache und Logik miteinander verbunden.
Ich weiss aber auch nicht was es jetzt bringt eine Anweisung durch eine andere zu ersetzen.
Pratisch (bis auf eventuell klarere Struktur usw) nichts. Weil man ja die Logik aquivalent bleibt (im Optimalfall). Theoretisch aber nutzt man damit ein Feature der Sprache, kann aber die Logik noch aquivalent implementieren. Diese ist ja selbst nichts weiter als in eine Struktur gequetschte Sequenz von Anweisungen, und die Struktur kann man austauschen, wie ich auch zuerst Milch in den Kaffee wie umgedreht schütten kann. Bestenfalls kommts aufs gleiche raus. Funktionen sind teil dieser Struktur. Würde ich diese Zugriffe auf Sprach Features (darauf läuft ja schlussendlich alles hinaus; und eine Ebene tiefer eben Zugriffe auf Hardware Funktionen, verschiebe in Register A, etc...) in Funktionen einlegen, ziehe ich mich doch praktisch "mit der eigenen Hand aus dem Sumpf".

Dagegen kann man es auch wie Io machen. Unterschied: Das geht nur auf "High-Level Ebene". Wie bei dem Beispiel mit dem Hardwarezugriff auf If und co kann man Sprachen erst ab einer bestimmten Ebene so gestalten, das es Exception Objekte gibt. Vermutlich, weil sie intern selbst auf eine andere Sprache und deren Anweisungen zurück greift, wenn ich zb eine Nachricht an ein Exception Objekt schicke. Ist aber nur meine Vermutung, die sich grade spontan ergeben hat.

Trotzdem kann man es auf die eine oder andere Art und Weise machen, was auch völlig ok ist. Anweisungen sind in "höheren" Sprachen natürlich etwas einschränkend und aus dem Design fallend, andererseits grenzen sie die Sprache von der Logik ab.
In Python kann man `goto` übrigens mit Ausnahmen nachbauen (http://entrian.com/goto/) und es sieht fast aus wie eine Anweisung. Smile
:o . Hab es mir mal angeschaut, ist aber nicht mit Ausnahmen gebaut (was mich auch gewundert hätte; man kann ja wirklich einfach "goto" wie ein Statement nutzen), sondern indem es direkt auf stdout zugreift und auf "goto" als String reagiert. Empfinde ich als unsauber; aber goto hat es auch nicht besser verdient :evil:
Deine Aufteilung in Python von Funktionen -> Programmlogik und Anweisung -> Sprachtätigkeiten die mit der Programmlogik nichts zu tun haben, hält bei bedingten Sprüngen nicht Stand.
Ja, man kann nicht ohne Anweisungen arbeiten. Insofern haben Anweisungen etwas mit der Logik zu tun, ja. Aber wie ich schon sagte: Es sind Sprach Tätigkeiten. Sie sind die "einfachste" Ebene, und if (+ den rest den man für einen if vergleich braucht) alleine vermag auch keine Wunder zu vollbringen. Insgesamt aber stellt die Sprache die Struktur für Logik bereit; die Logik wiederrum baut sich aus sich selber auf, also anderen Objekten / Funktionen / whatever. Auf der unteresten Ebene stehen Statements (und Speichermöglichkeiten). Insofern hatte ich Unrecht (bzw mich unklar ausgedrückt). Aber: Das wichtige ist, das all das nur Struktur ist, die die Sprache bereitstellt. Die eigentliche Logik sind ja pure Gedanken, um es so zu sagen. Diese bauen sich aus den einfachsten Dingen auf. Diese Dinge sollten aber klar austauschbar sein; manchmal auch nicht vorhanden. All das fügt sich dann in eine Struktur ein, die die Sprache definiert (Funktionen, ...). Aber man könnte theoretisch das ganze Gestrüpp auch in zb BASIC oder Assembler schreiben. Füge ich nun dinge wie "for" direkt als Funktion ein, zwänge ich die einfachste Logik (die keine Gedankenlogik ist; sondern eben "Boolsche-Logik" - einfachste Dinge eben) in diese Sprach Struktur (Funktion) rein, ist das bei höheren Sprachen erstmal in Ordnung. In dem Moment verbinde ich aber die Logik mit der tieferliegenden, einfachen Logik, indem ich die einfache Logik ja selber wieder als Gedankenlogik sehe, die in diese Sprachstruktur reingepresst gehört.

Wo fangen bei Dir Sprachinterna an und was ist direkter Zugriff darauf?
Alles, was in die Programmlogik der Sprache selber gehört, ist ein Sprachinterna. Alles, was kein Sprachinterna ist, ist folglich in die von der Sprache festgelegte Struktur eingebettet.
Ob ich nun eine ``raise``-Anweisung verwende oder eine `raise`-Methode ist doch egal, ich habe in beiden Fällen nichts mit der Implementierung zu tun, die ein Sprachentwickler geschrieben hat.
Ist ja auch so, ist nur ein anderer Herangehensweg.
Ich sehe auch grade einen Denkfehler von mir: ein Exception Objekt ist in der eigenen Sprache mindestens dargestellt, beugt sich also der Struktur der Sprache. Insofern ist eine Methode darauf auch ein gangbarer Weg. Aber irgendwo ist auch da die Grenze zwischen Sprachen-Logik und deren Struktur, sowie der von ihr für die neue, in ihr geschrieben Sprache, definierte Struktur, in die man dann "Gedankenlogik" einbettet. Je weiter man sich öffnet, desto näher kommt man dadran heran.

Entweder man bricht mit der Struktur seiner Sprache oder man versucht, fundamentale Dinge dieser Struktur in die eigene Struktur hineinbringen. Was ja auch mittels Abstraktion funktioniert, wodurch man halbwegs oberflächlich auf der Api der Sprache operieren kann... Ich glaube, ich habe die ganze Zeit von zwei verschiedenen Dingen gesprochen :?

Nun, dennoch, irgendwo ist der Punkt, an dem sich eine Sprache "selber auf dem Sumpf ziehen müsste", würde man Anweisungen in die Sprachstruktur reinzwängen. Je tiefer (näher an Hardware Ebene) man geht, desto näher ist man diesem Punkt.



Ich habe das nun alles so aufgeschrieben, wie es mir in die Gedanken kam. Da ich aktuell aber erstmal genug habe, poste ichs so.

EDIT:

Wo ich grade bei Hardware und deren Funktionen war: Gibts da eigentlich eine ganz gute Beschreibung zu, wie das in etwa funktioniert? Also nicht das Nullen und Einsen für An / Aus zeug, sondern wie konkret spricht Assembler die Hardware an? Und wie wird in Maschinencode eine Endlosschleife dargestellt? Würde mich interessieren.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

str1442 hat geschrieben:Wo ich grade bei Hardware und deren Funktionen war: Gibts da eigentlich eine ganz gute Beschreibung zu, wie das in etwa funktioniert? Also nicht das Nullen und Einsen für An / Aus zeug, sondern wie konkret spricht Assembler die Hardware an? Und wie wird in Maschinencode eine Endlosschleife dargestellt? Würde mich interessieren.
Assembler spricht Hardware gar nicht an. Assembler wird zu Maschienencode assembliert und dieser wird von der jeweiligen CPU interpretiert. Du kannst dir dazu das Intel Architecture Software Developer’s Manual, Volume 2: Instruction Set Reference ansehen. Dort siehts du die Klasse der Sprungbefehle Jcc, die an die angegebene Adresse springen (natürlich steht nicht Jcc im Machienencode sondern die Binäre Darstellung des Befehles, die du ebenfalls in den Dokument nachschlagen kannst). Dazu setzen sie den Instruction Pointer (bei x86 wäre der in EIP) auf die Adresse und beim nächsten Maschienenbefehlszyklus wird von dort der nächste Befehl gelesen und ausgeführt. Alternativ wenn du keine Bedingung zum Prüfen hast, kannst du auch direkt JMP verwenden. Sieht in Pseudo-Code etwa so aus:

Code: Alles auswählen

0 ADD EAX,2
1 JMP 0
Du musst dann nachschauen was dass für ein ADD-Typ ist, und was für ein JMP-Typ und kannst dann das Programm quasi von Hand Assemblieren.

Edit: hier das in x86-Assembler (NASM):

Code: Alles auswählen

    section .text
global  _start
_start:
        add     eax,2
        jmp     _start
Wobei _start dann der Adresse von 0 entspricht.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Bin über diese Aussage gestolpert ohne jetzt im Detail zu prüfen, ob sie danach nochmals diskutiert wurde:
Wenn Du es auf das möglichst wenig beschränken willst (ohne bei Brainfuck zu landen), braucht man so etwas wie Funktionsaufrufe und eine bedingte Auswertung. Alles andere ist syntaktischer Zucker.
Betrachtet man Funktionen als Objekte und Funktionsaufrufe als Nachrichten an Objekte, die Methoden ausführen, braucht man nichts weiter, wie ich am Beispiel von Smalltalks bedingten "Anweisungen" gezeigt hatte. Für Schleifen braucht man entweder Rekursion oder eine spezielle Nachricht `_Restart` wie es Self machte.

Betrachtet man die Welt funktional, braucht man ebenfalls keine bedingten Ausdrücke. Funktionen höherer Ordnung (für Bedingungen) und Rekursion (für Schleifen) reichen aus.

Code: Alles auswählen

# Mir egal, dass ich Python-Schlüsselworte benutze
def if(cond, then, else):
    return cond(then, else)

def true(then, else):
    return then()

def false(then, else):
    return else()

# Annahme: 3 < 4 liefert entweder true oder false
# Das 1/0 löse eine Ausnahme aus, wenn der Ausdruck ausgewertet wird
if(3 < 4, lambda:1, lamda:1/0)
Ich finde Io als Beispiel übrigens nicht so gelungen, weil die Sprache eine sehr merkwürdige "call by name" Auswertung ihrer Argumente durchführt, um Dinge wie "if" in der Sprache zu definieren. Das erscheint mir von der Theorie her unsauber und ist schwer und nur mit Implementationsdetails zu beschreiben. Der hier gezeigte funktionale Ansatz, den man etwa in der kaum bekannten Lehrsprache Pico wiederfindet, ist IMHO eleganter. Self, wovon Io abgeschaut ist, finde ich auch eleganter, allerdings ist auch hier der Kern nicht so einfach wie das funktionale Beispiel.

Ob ein Prozessor übrigens Anweisungen oder Funktionen hat, ist eine interessante Definitionsfrage. Man kann jede Instruktion als eine Funktion verstehen, die den gesamten Prozessorzustand als Eingabe und einen neuen Zustand als Ausgabe hat. Dann ist es eine Funktion. Das scheint mir ein sinnvolles und nicht so realitätsfernes Modell zu sein. Es ist jedenfalls ganzheitlicher als die Idee, Zustand getrennt von Anweisungen, die daran jeweils inkrementelle Änderungen vornehmen, zu betrachten. Gerade, wenn man die massiv parallelen Vorgänge in modernen Prozessoren irgendwie verstehen will.

Statt dagegen zu argumentieren, ist es IMHO hilfreich, sich einfach mal der abstrakteren (und uniformeren) Sichtweise zu öffnen und Dinge unter diesem Gesichtspunkt zu betrachten. Es sind verschiedene Denkschulen, aber sie beeinflussen stark, wie man sich bestimmten Problemen nähert und erweitern den Horizont.

Stefan
BlackJack

@sma: Die Aussage mit dem Beschränken war darauf bezogen, dass die ganzen Anweisungen in Python laut str1442 "Sprachtätigkeiten" sind und nichts mit der Programmlogik zu tun haben.

Dass Anweisungen grundsätzlich nicht notwendig sind, ist glaube ich allen Beteiligten klar.

Was meinst Du bei Io mit den Implementierungsdetails? Ich finde das lässt sich mit (verketteten) Nachrichten, die an Objekte geschickt werden und erst beim Empfänger ausgewertet werden (oder eben auch nicht) ganz gut beschreiben.

Die Frage Anweisung oder Funktion kann man überall so oder so beantworten. Python-Anweisungen wie ``def`` oder ``class`` kann man als Funktionen auffassen, bei denen implizit der gesamte Interpreterzustand reingeht und ein veränderter wieder heraus kommt. Siehe Monaden in Haskell. Alles ist Funktion, zur Not nehmen wir den Zustand des Universums vor dem Aufruf und nach dem Aufruf als Werte. ;-)

In der Regel werden Assembler-Befehle halt als Anweisungen beschrieben.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Da alles etwas aus dem Ruder gelaufen ist, fasse ich kurz nochmal zentral alles gesagte zusammen:

1. Jede Sprache kann eine API haben, auf die man zugreifen kann. Inwieweit man auf sie zugreifen kann, hängt von der Sprache ab.

2. Mithilfe von Anweisungen: Das ist möglich und kann helfen, das Aussehen der Sprache zu "generalisieren". Andererseits sind sie irgendwo einschränkend und nicht unbedingt von Sinn.

3. Ohne Anweisungen, dafür mit dieser eigenen API. Etwa durch Exception Objekte usw.

4. Diese API für die Sprache ist in dieser Sprache selber implementiert. Die Sprache, auf der diese Sprache basiert, kommt nicht ins Spiel.

Sofern das soweit richtig ist:

1. Die Sprache legt eine Struktur fest, in die sich in ihr geschriebener Quellcode einzuordnen hat. Zum Beispiel durch Funktionen, Klassen, Objekte. Keines dieser Konstrukte muss aber existieren und es kommt auf die Sprache an. Irgendwo ist üblicherweise dann ein Übergang zu der Sprache, in der diese Sprache geschrieben wurde, auf den man nur über die Sprachapi zugreift (oder Anweisungen) zugreift.

2. Logik existiert erstmal nur in den Köpfen der Entwickler. Diese muss dann in die Struktur der Sprache gebracht werden (zb indem man Funktionen definiert). Diese wiederrum kann aus anderen Konstrukten der Sprache bestehen (zb Klassen), womit sich am Ende eine Grundessenz der API der Sprache oder deren Statements ergibt.

3. Statements sind also die kleinsten Teile einer Sprache. Sie sind *nicht* außerhalb der Logik, aber die *richtige* Logik wird durch die Struktur der Sprache *und* ihren Anweisungen / ihrer API bestimmt.

Die API kann also zb soetwas wie in Io sein. Wichtig ist nur, das die API von der eigenen Sprache voll zugänglich ist.

Soweit das stimmt:

1. Anweisungen / API sind ihrerseits in der Sprache oder in der Sprache, in der die eigentliche Sprache geschrieben wurde, implementiert. Somit muss sich diese Sprache in die Struktur ihrer "Muttersprache einfügen." Diese wiederrum benutzt selber Anweisungen und / oder eine API.

2. Das geht immer so weiter, bis man auf (fast) Hardware Ebene ankommt.

3. Dort nun wiederrum muss man sich in die Struktur der CPU einfügen, die so direkt wohl nur eine sehr einfache kennt. Das Benutzen dieser Struktur mittels Maschinencode geschieht mittels Anweisungen an die CPU, die dann auf Hardwareebene ausgeführt werden.

Wäre diese Anweisung nun eine Funktion, hätte die CPU selber eine Struktur und mindestens eine Art Funktion definiert. Wo soll die herkommen? Ansonsten könnte man soetwas sogar als Funktion ansehen, die den kompletten Status der CPU erhält und verändert zurückgibt. Aber das ist wohl nur theoretisch so.

Conclusion:

1. Statements sind auf CPU Ebene vorhanden, was auch immer sie nun genau sind.

2. In höheren Sprachen sind Statements allerhöchstens dazu gut, um Sprachfeatures auf diese Art und Weise zugänglich zu machen. Alternativ kann man eine API definieren und dort auf die Sprache zugreifen. Zugriff auf die Sprache, in der die Sprache implementiert ist, ist *nicht* möglich.

3. Jede Sprache definiert eine Logikstruktur, und Funktionen sind Teil dieser Logikstruktur. Die Logik selbst ist vielseitig, und die genaue Umsetzung kann mittels verschiedener, eventuell speziellen Sprachstrukturen variieren. Den kleinsten Teil der Logik bildet die API der Sprache oder aber die Statements, mit deren Hilfe aus sie zugegriffen wird.

4. Funktionen sind Teil einer Sprache als Logikstruktur. Anweisungen sind in höheren Sprachen eher sinnlos, da man alles in ihrer eigenen Logik Struktur abbilden könnte.

5. Funktionen sind nichts allumfassendes, sondern eine Sprachstruktur und vom Sprachdesign abhängig. Anweisungen sind allerdings zumindest in niedrigeren Sprachen, weil vorgegeben durch die Hardware, da. Insofern ist die Trennung zwischen Funktion / Statement *innerhalb* einer höheren Sprache sinnlos, bei Hardwarenahen Sprachen dagegen nicht. *Auf alle Sprachen* angewandt sind Funktionen Teil der einzelnen Sprachstruktur, während zumindest die CPU Anweisungen überall vorhanden sind. Irgendwo.


Insofern habe ich meine Sichtweise auch geändert. Das man Statements klar von Funktionen trennen kann, ist wahr, wenn man alle Statements anschaut. Sind ja in vielen Sprachen vorhanden. Insofern mag es in Python nicht zutreffend sein, das man da unterscheiden kann, da es aber zb das return Statement ja auch in C gibt, stellt sich hier die Frage, wie stark in C ein Statement mit einem "Hardware-Statement" verbunden ist. Insofern habe ich falsch pauschalisiert.

Funktionen sind allerdings Strukturen einer Sprache, und solche Strukturen ändern sich von Sprache zu Sprache. Irgendwo, und sei es nur im Ausgeführten Code, sind aber die "CPU Statements".


@all

Durch die Zusammenfassung ist hoffentlich klarer geworden, inwiefern ich meine Argumentation geändert habe, und was ich mit der Logikstruktur usw meinte. Durch die langen Texte habe ich mich da ein wenig verrannt.

@Leonidas:

Also kann man sagen, das grundlegende Befehle praktisch in die CPU "reingegossen" werden? Gut, das war ja vorher klar, aber das es doch so umfangreich zu sein scheint (bisher dachte ich, rechnen mittels Dual System und "Register" seien so ziemlich das einzige), habe ich nicht erwartet. Das Dokument ist ganz gut zum (großzügig) durchblättern, um den Gesamtzusammenhang zu verstehen. Insofern ist eine CPU ja wirklich nur das direkte (und da war ich mir vorher nicht sicher) Hardware Aquivalent zu Assembler, das mittels EVA Prinzip seine vielen kleinen Transistoren an oder aus schaltet, und dabei bei Konstellation xy z auslöst. Danke.
BlackJack

@str1442: Du hättest mit Unternummerierungen arbeiten sollen, bei den vielen Listen die immer wieder mit 1. beginnen. ;-) Ich nummeriere die Listen in Gedanken nochmal römisch durch. :-)

zu II.3.: Statements kann man IMHO nicht als die kleinsten Teile einer Sprache bezeichnen, da sie wie zum Beispiel ``class`` in Python sehr komplexe Dinge erzeugen können. Als kleinste Teile würde ich eher Sachen beschreiben, die man so nicht mehr in der Sprache selber schreiben könnte, also zum Beispiel die Operationen auf den Grunddatentypen wie die Rechenoperationen auf `float`\s. Also man könnte natürlich eine Klasse basteln, die `float` mit `int`\s implementiert, aber eben nicht die IEEE-Hardware-Fliesskommazahlen. Ich hoffe es ist klar was ich meine.

zu IV.2.: Es gibt durchaus Hochsprachen, bei denen man auf tiefere Schichten durchgreifen kann. Zum Beispiel kann man in (Turbo|Borland|Free)Pascal und D auch direkt im Quelltext Assembler einflechten.

CPUs sind auch nicht mehr so einfach wie sie mal waren. In modernen CISC-CPUs werden die CISC-Befehle oft intern nochmal in Mikrocode umgesetzt, der aus einem RISC-Befehlssatz besteht. Bei einigen Prozessoren kann man Mikrocodeprogramme in den Prozessor laden, beim Crusoe von Transmeta muss man das sogar tun, damit der Prozessor weiss was er tun muss/kann. Der CPU-Interne "Compiler" kann auch Befehle eigenständig in der Reihenfolge umsortieren wenn dadurch die verschiedenen Recheneinheiten besser ausgelastet werden und ein höherer Befehlssatzdurchsatz dadurch erreicht werden kann.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

str1442 hat geschrieben:Also kann man sagen, das grundlegende Befehle praktisch in die CPU "reingegossen" werden?
Jein. Ab einer bestimmten Stelle schon aber das muss nicht die Maschienensprache sein. Wie BlackJack sagte betreiben viele CISC-CPUs unter anderem die üblichen Desktop-CPUs von Intel eine weitere Rekompilation intern, wobei die komplexen CISC Befehle dann gegen mehrere, einfachere RISC-Befehle ausgetauscht werden. Wenn du dir ansiehst, wie viele Möglichkeiten es gibt bestimmte Statements wie MOV zu nutzen (32-Bit Register nach Memory, Memory nach 32 Bit Register, 32 Bit Register in 32 Bit Register, das ganze nochmal mit 16 Bit und nochmal mit 8 Bit, und dann noch einige weitere Fälle) gibt das allein bei MOV schon viele Möglichkeiten. Wie es nun intern abläuft weißt du nicht und kannst darauf zumindest bei x86 keinen Einfluss haben. Die Intel-Manuals sagen dir aber dass MOV diesen und jenen Effekt hat. Wie das dann intern ausssieht, ob das jetzt in irgendeine Pipeline gesteckt wird oder ob auf den vorherigen Befehl gewartet werden muss weil du Datenabhängigkeiten hast, dass kann dir als Assembler-Programmierer egal sein. Wenn die CPU etwas in bestimmten Fällen nicht so auswertet wie angegeben ist das ein Bug.
str1442 hat geschrieben:Gut, das war ja vorher klar, aber das es doch so umfangreich zu sein scheint (bisher dachte ich, rechnen mittels Dual System und "Register" seien so ziemlich das einzige), habe ich nicht erwartet. Das Dokument ist ganz gut zum (großzügig) durchblättern, um den Gesamtzusammenhang zu verstehen.
Ja, ich wollte eigentlich das aktuelle Manual verlinken habe die aktellere Version aber auf die Schnelle nicht gefunden. Wäre aber auch unnötig komplex gewesen, da dort dann auch RAX & Co, also die x86_64-Sachen auch noch drin wären, die nur von geringem Interesse sind.
str1442 hat geschrieben:Insofern ist eine CPU ja wirklich nur das direkte (und da war ich mir vorher nicht sicher) Hardware Aquivalent zu Assembler, das mittels EVA Prinzip seine vielen kleinen Transistoren an oder aus schaltet, und dabei bei Konstellation xy z auslöst.
Sie gibt sich als sowas aus, ber mittels interner Optimizationen ist das nicht unbedingt der Fall. Das ist wie bei einem Compiler der etwa rekursive Aufrufe intern in Schleifen umwandelst, du aber davon nichts mitbekommst.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

zu II.3.: Statements kann man IMHO nicht als die kleinsten Teile einer Sprache bezeichnen, da sie wie zum Beispiel ``class`` in Python sehr komplexe Dinge erzeugen können. Als kleinste Teile würde ich eher Sachen beschreiben, die man so nicht mehr in der Sprache selber schreiben könnte, also zum Beispiel die Operationen auf den Grunddatentypen wie die Rechenoperationen auf `float`\s. Also man könnte natürlich eine Klasse basteln, die `float` mit `int`\s implementiert, aber eben nicht die IEEE-Hardware-Fliesskommazahlen. Ich hoffe es ist klar was ich meine.
Gut, könnte man sagen. Allerdings meinte ich an der Stelle die API der Sprache (wovon man die grundlegensten Dinge, wie auch in Python, als Statements abbilden kann), habe wohl beim Schreiben zu schnell gedacht.
zu IV.2.: Es gibt durchaus Hochsprachen, bei denen man auf tiefere Schichten durchgreifen kann. Zum Beispiel kann man in (Turbo|Borland|Free)Pascal und D auch direkt im Quelltext Assembler einflechten.
Ja, aber diese Abschnitte werden ja vom jeweiligen Kompiler praktisch assembliert und "dazugelinkt". Ich meinte den Zugriff über die Struktur der Sprache hinweg. Also wenn ich in Python über ein MagicObject plötzlich auf C zugreifen könnte, und dann im Python Quellcode in einer Struktur schreibe, wie C sie festlegt (und auch C Obje... äh... Strukturen? benutzen könnte). Damit würde ich ja alle Strukturierungen von Python quasi komplett abschalten können. Deswegen muss man ja den Umweg über ctypes / Cython gehen, und erst Objekte in C designen, die sich dann in die Strukturierung von Python einfügen lassen (per import).

@CPU's: Alles klar, dank euch.
BlackJack

@str1442: Also mit `ctypes` kann man schon ganz gut in den Interna von CPython rumpfuschen. Man hat damit freien Zugriff auf die `libpython.(so|dll)`. :-)

Die Beschreibung "assembliert und vom Kompiler dazugelinkt" würde ich bei den genannten Pascal-Varianten und D nicht verwenden. Das klingt nach getrennten Object-Dateien. Der Assemblerquelltext kann da aber tatsächlich mitten in einer Funktion oder Prozedur zwischen Quelltext in den Sprachen stehen und man hat auch Zugriff auf die in Pascal bzw. D deklarierten Variablen.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

@str1442: Also mit `ctypes` kann man schon ganz gut in den Interna von CPython rumpfuschen. Man hat damit freien Zugriff auf die `libpython.(so|dll)`. Smile
Oha, gut, das ist natürlich nett. Sollte man aber sicher nicht fahrlässig nutzen :D
Die Beschreibung "assembliert und vom Kompiler dazugelinkt" würde ich bei den genannten Pascal-Varianten und D nicht verwenden. Das klingt nach getrennten Object-Dateien. Der Assemblerquelltext kann da aber tatsächlich mitten in einer Funktion oder Prozedur zwischen Quelltext in den Sprachen stehen und man hat auch Zugriff auf die in Pascal bzw. D deklarierten Variablen.
Joa, aber solang man nicht anfängt, wild auf zig Registern zu operieren, ist der Assemblercode recht gut abgegrenzt. Würde man das nämlich machen, würde man man mithilfe einer hardware-näheren Sprache die spracheigene Strukturierung umgehen. Das macht dann vermutlich weniger Spaß ;)

@Io:

Hab mir die Sprache mal angeschaut, und bin ziemlich begeistert. Hab mir daraufhin mal den Guide angeschaut, und war da auch ziemlich schnell durch. Einzig manche der Methoden (bzw Message) Namen finde ich ein wenig aus der Reihe fallend bis willkürlich (zb bei Range das to(x)), aber ansonsten hört sich das alles sehr nett an. Viele der Funktionen scheinen Python Funktionen zu ähneln ('"Test String" split join("\n") println' hat auf Anhieb geklappt, oder strip etc), gibt auch ein Python Addon, wobei das undokumentiert ist. Schade nur, das sich google & co zu "Io Language" nicht bemühen lässt :D. Vielleicht mal mit einer Metasuchmaschine probieren.

ADD: Grade getestet: Yahoo lässt Suchen auf Wörter mit weniger als 3 Wörtern zu.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Zu Io siehe auch meine Version in Python.

Ansonsten: Das "1 to(10)" um ein Range-Objekt zu erzeugen, stammt von Smalltalks "1 to: 10" und "1 to: 10 do: aBlock", was sich doch recht gut liest. Bei Io ist die Syntax etwas uniformer, da muss man eben Klammern benutzen.

Stefan
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

lunar hat geschrieben:Bleibt nur noch die Frage, an was du nachts um zwei gedacht hast, wenn dieses Wort den Weg in dein Posting gefunden hat ;) Ich glaube jetzt einfach mal, dass es nur eine fehlgeleitete Übersetzung des englischen Silicone war ;)
Freud lässt grüßen ;)

Aber *wenn* es eine naheliegende fehlgeleitete Übersetzung eines englisches Wortes war, dann wohl von "silicon" und nicht "silicone" *in Haarspalter-Reihe anstell* :)


Und wo ich schon mal hier bin, gebe ich noch den Link zum Besten, wegen dem ich überhaupt in dieses Thema geschaut habe: Ingredients for building a DSL in Python (Episode I).
lunar

Y0Gi hat geschrieben:Aber *wenn* es eine naheliegende fehlgeleitete Übersetzung eines englisches Wortes war, dann wohl von "silicon" und nicht "silicone" *in Haarspalter-Reihe anstell* :)
D'oh ... freud'scher Verschreiber ;)
Antworten