Per Empfehlung reposte ich dies von Stack Overflow .
Kürzlich habe ich über folgendes Thema nachgedacht.
Betrachten Sie den Code für eine Standard "Hallo Welt!" Programm:
main()
{
printf("Hello World");
}
Nahezu jede Änderung an diesem Code macht ihn nun völlig unbrauchbar. Tatsächlich verhindert fast jede Änderung, dass der Code kompiliert wird. Beispielsweise:
main(5
{
printf("Hello World");
}
Nun zur eigentlichen Frage. Gibt es eine Programmiersprache, in der jede mögliche Kombination von Symbolen - also jeder Ausdruck - Sinn macht? Ich versuchte, über eine Lösung nachzudenken, und fand zwei:
Postfix mit einer begrenzten Anzahl von Variablen. Im Wesentlichen sind alle Variablen bereits definiert, bevor Sie Code schreiben, und Sie müssen nur damit arbeiten. Theoretisch können Sie eine beliebige Anzahl von Operationen ausführen, indem Sie eine Kette von vielen einfachen Programmen bilden, von denen jedes die Ergebnisse an andere weitergibt. Code kann als eine Reihe von Zeichen in Postfix-Notation geschrieben werden.
"Postfix" mit einem Stapel von Variablen. Variablen werden auf einem Stapel gespeichert. Jede Operation nimmt zwei Variablen von oben und setzt das Ergebnis an ihre Stelle. Das Programm endet, wenn es die letzte Operation oder Variable erreicht hat.
Ich persönlich hasse beide. Sie sind nicht nur begrenzt, sie sind auch unelegant. Es handelt sich nicht einmal um echte Lösungen, sondern vielmehr um Problemumgehungen, die im Wesentlichen einige Arbeiten an einen externen Prozess "verlagern".
Hat jemand eine andere Idee, wie man dieses Problem löst?
quelle
You are a bimbo.
[
]
Befehle (laut Wiki-Seite). Mein Gedanke war, die CPU-Opcodes zu betrachten. Aber selbst dann können einige Muster zu Problemen führen (z. B. wenn ein Opcode 3 Bits umfasst, Ihr Programm jedoch nur 2 Bits). Abgesehen von diesem Problem, bei dem möglicherweise mit zusätzlichen 0 Bits aufgefüllt wird, kann man an jede CPU mit einem denken vollständiger Opcode-Satz, der den Anspruch "Jeder String ist ein gültiges Programm" erfüllt. Vielleicht bedeutungslos, aber immer noch gültig.Antworten:
Redcode, die Assemblersprache hinter Codewars, wurde ausdrücklich so geschrieben, dass sie nur sehr wenige Stoppanweisungen enthält, da der Code häufig verstümmelt wird, bevor er endgültig veröffentlicht wird. Je mehr Möglichkeiten er zum Stoppen hat, desto weniger interessant ist das Spiel.
In der Praxis gibt es nur sehr wenige solcher Sprachen, weil wir nicht nur wollen, dass ein Programm ausgeführt wird, sondern dass es so läuft, wie wir es erwarten. Wenn Sie einen Tippfehler machen und die Art und Weise ändern können, wie das Programm ausgeführt wurde, muss es annehmbar nahe am ursprünglich erwarteten Verhalten liegen, oder die Programmierer sind frustriert.
Es gibt einen gewissen Vorrang für solche Dinge, wenn natürliche Sprachen anstelle von formalen Sprachen verwendet werden, aber ich würde es nicht als großes Feld bezeichnen, wenn man es mit der Verwendung formaler Sprachen vergleicht. Wenn Sie sich für solche Programmiersprachen interessieren, ist die Community für die Verarbeitung natürlicher Sprachen genau das Richtige für Sie.
Ein weiteres Gebiet, das Sie sich ansehen könnten, ist die Genetik. Es gibt bemerkenswert wenige genetische Sequenzen, die einfach ungültig sind. Viele von ihnen sind bei Reproduktionen nicht sehr effektiv, aber nur sehr wenige sind ungültig.
quelle
replicate this string
. Es ist jedoch keine wirklich aussagekräftige Programmiersprache, da es bei weitem nicht in der Nähe von Turing Complete liegt.Die Idee einer universellen Turingmaschine verwendet genau eine solche "Programmiersprache": eine Codierung von Turingmaschinen als natürliche Zahlen, beispielsweise binär dargestellt, so dass jede natürliche Zahl eine Turingmaschine, dh ein Programm bezeichnet. In dieser Sprache ist jede Folge von Nullen und Einsen ein Programm.
Ich bin mir sicher, dass es auch esoterische Programmiersprachen gibt, in denen jeder String ein Programm ist. Wenn Sie jedoch nur nach einer Liste dieser Fragen fragen, ist Ihre Frage hier meines Erachtens nicht zum Thema.
quelle
Eine Programmiersprache so zu erweitern, dass jeder Ausdruck Sinn ergibt, ist immer möglich, aber nicht interessant. Sie können beispielsweise jedem Ausdruck, den die Originalsprache ablehnt, einfach die Bedeutung „Nichts tun“ zuweisen.
Das Entwerfen einer Programmiersprache, in der jeder Ausdruck so sinnvoll ist, dass Sie ihn ausführen können, ist nicht besonders nützlich. Eine gute Programmiersprache ist nicht nur eine, in der ein Affe auf einer Tastatur tippen und ein gültiges Programm schreiben kann, sondern eine, in der ein Programmierer leicht das Programm schreiben kann, das er schreiben möchte. Das Schreiben gültiger Programme ist nicht der schwierige Teil des Programmierens: Der schwierige Teil ist das Schreiben eines Programms, das das ausführt, was von ihm erwartet wurde. Das Ablehnen offensichtlich falscher Programme ist in dieser Hinsicht sehr hilfreich.
Eine andere Möglichkeit, dies zu beheben, besteht darin, die Semantik aller möglichen Eingaben vollständig zu definieren, einschließlich der Angabe, welche Kompilierungs-, Lade- oder Laufzeitfehler für jede Eingabe generiert werden sollen, falls vorhanden. Das heißt, "Programm nach dem Drucken
Syntax error at line 42
auf dem Standardfehlerstrom abbrechen " ist Teil der definierten Semantik der Sprache. Jeder Ausdruck "macht Sinn", indem er eine definierte Bedeutung hat. Ist das eine nützliche Bedeutung? Vielleicht - schließlich ist es nützlich, wenn das Programm offensichtlich falsch ist, es abzulehnen.quelle
Schauen Sie sich Jot an , eine auf kombinatorischer Logik basierende Sprache, in der jede Folge von 0en und 1en (einschließlich einer leeren Folge) ein gültiges Programm ist.
quelle
Ein gutes Beispiel ist Whitespace . In der eigentlichen Sprache ist jede Kombination von Operatoren gültig. Die Operatoren sind Leerzeichen, Tabulator und Zeilenvorschub (insbesondere "\ n"). Alle anderen Zeichen gelten als Kommentare .
Diese Antwort und in der Tat Ihre Frage (sowie die gesamte Webseite) sind Beispiele für gültige Whitespace-Programme (obwohl sie möglicherweise nichts besonders Interessantes bewirken).
quelle
[LF][Tab][LF]
) Was passiert, wenn Sie einen leeren Stapel platzieren? Was passiert, wenn Sie zu einem undefinierten Label springen? Was passiert, wenn Sie doppelte Bezeichnungen definieren?Ich möchte auf die Idee eingehen, die viele Plakate gegeben haben, dass eine solche Sprache "nutzlos" wäre. Vielleicht wäre es für Menschen nutzlos, manuell zu schreiben, um eine bestimmte Aufgabe zu lösen. Dies ist jedoch sicherlich nicht der einzige Anwendungsfall, obwohl er in der Mehrzahl der Fälle für Programmiersprachen verwendet wird. Einige Anwendungsfälle kommen in den Sinn, wenn eine solche Sprache nützlich ist, und wir können diese Felder nach Beispielen für solche Sprachen durchsuchen.
Erstens ist Cort Ammons Anspielung auf die Genetik genau richtig : Die Programmtransformation in der Frage (als Ersatz
)
für5
) kann als Mutation angesehen werden . Diese Art der Manipulation ist auf dem Gebiet der Evolutionsberechnung üblich ; insbesondere genetische Algorithmen führen solche Transformationen auf Saiten , während die genetische Programmierung Transformationen Programme . In beiden Fällen möchten wir normalerweise jeder Möglichkeit eine Bedeutung zuweisen, da dies den kompaktesten Suchraum ergibt.Genetische Algorithmen basieren auf einer Art Bewertungsfunktion für Zeichenfolgen. Wenn wir einen Programmiersprachen-Interpreter als Auswertungsfunktion verwenden, haben wir ein Szenario, in dem eine Programmiersprache nützlich ist, die allen möglichen Zeichenfolgen eine Bedeutung zuweist. In der genetischen Programmierung wird angenommen, dass unsere Bewertungsfunktion ein Programmierspracheninterpreter ist, wir können jedoch verschiedene Darstellungen für unsere Programme auswählen . Beispielsweise arbeiten viele Systeme mit abstrakten Syntaxbäumen. Wenn wir Zeichenfolgen als Repräsentation auswählen, stellen wir dasselbe Szenario wieder her wie bei genetischen Algorithmen.
Eine andere Situation, in der jeder String ein gültiges Programm sein soll, ist die Aufzählung von Programmen. Dies hängt mit der von CodesInChaos erwähnten Bijektion zusammen, aber wir ziehen es aus mehreren Gründen möglicherweise vor, Zeichenfolgen anstelle von natürlichen Zahlen zu verarbeiten:
In Bezug auf Beispielsprachen basieren viele evolutionäre Rechensysteme auf Stack-Sprachen wie der Push- Familie. Diese neigen dazu, beliebige Ströme von Token zuzulassen (die wir als einzelne Zeichen darstellen könnten). Manchmal (wie im BrainSlugs83-Beispiel für Brainfuck) gibt es Einschränkungen beim Ausgleichen von Klammern. jedoch können wir dies in Beziehung zu sich selbst begrenzenden Programme , dass ein String wie
[
kein gültiges sein Programm , aber es ist ein gültiges Programm Präfix . Wenn wir uns einen Compiler / Interpreter vorstellen, der den Quellcode von stdin liest, dann lehnt er einen String nicht ab[
, sondern wartet einfach auf weitere Eingaben, bevor er fortfährt.Sprachen wie Binary Combinatory Logic und Binary Lambda Calculus sind direkt aus der Arbeit an algorithmischer Informationstheorie entstanden, z. von http://tromp.github.io/cl/cl.html
quelle
Echte Programmiersprachen sollen dem Menschen Sinn vermitteln , nicht dem Computer. Da viele lustige Texte mit fast zufällig gemischten Buchstaben in der Show herumschwirren, können die Leute Kauderwelsch lesen und daraus einen Sinn ziehen, auch ohne das Zerfleischen zu bemerken. Denken Sie nur zurück, wie schwierig es ist, Tippfehler und andere solche Fehler in Texten zu finden.
Eine Programmiersprache wie die, nach der Sie fragen, würde den Leuten verständlich machen, was sie lesen möchten, und nicht, was aufgeschrieben ist. Das Debuggen in Sprachen, in denen es eine begrenzte Anzahl von gesetzlichen Bestimmungen gibt, bei denen nicht viel Unklarheit möglich ist, ist bereits schwierig genug. Gute Sprachen reduzieren mögliche Interpretationen zB durch transponierte Symbole oder Tippfehler. Natürliche Sprachen sind aus dem gleichen Grund auch für ihre Redundanz berüchtigt.
quelle
In der Programmiersprache Brainfuck kann fast jeder mögliche binäre Ausdruck als Programm interpretiert werden. - Das heißt, Sie könnten ein völlig gutes Programm nehmen, eine Menge Müll hinein tippen und es wäre immer noch ohne Probleme kompilierbar / interpretierbar.
( Bearbeiten: Es stellt sich heraus, dass Sie öffnende und schließende eckige Klammern abgleichen müssen, aber ansonsten gilt das Obige.)
Dies wird durch diese zwei einfachen Methoden erreicht:
Alle Befehle, die es versteht, sind ein einzelnes Byte (in BF sind sie alle einzelne ASCII-Zeichen, zum Beispiel *).
Alle Zeichen, die es nicht versteht, werden als Kommentare verworfen.
Die Programmiersprache ist Turing complete (das heißt, sie kann alles, was jede andere Sprache kann).
*: Es stellt sich heraus, dass nicht alle BF-Befehle ein einzelnes ASCII-Byte sind - dh Klammern MÜSSEN übereinstimmen -, so dass diese Sprache dort nicht die ersten Kriterien erfüllt. - Aber jede Sprache, die beide Kriterien erfüllt, würde das erfüllen, was das OP verlangt.
quelle
]
am Ende der Quelle genügend Zeichen vorhanden sind , um allen nicht übereinstimmenden[
s zu entsprechen, und[
am Anfang genug , um allen nicht übereinstimmenden zu entsprechen]
. Die Semantik von[
und]
kann leicht geändert werden, um sie dementsprechend zu machen. (zB wenn kein passender gibt es]
dann[
stoppt nur die Ausführung , wenn das Byte an dem Datenzeiger Null ist.]
springt nur zu Beginn des Programms in einer ähnlichen Situation.) Die resultierende Sprache würde komplett sein Turing und eine beliebige Zeichenfolge akzeptieren würde.