Diese Herausforderung basiert auf Helka Hombas Frage Programmieren einer unberührten Welt . Aus dieser Frage heraus lautet die Definition eines makellosen Programms:
Definieren wir ein unberührtes Programm als ein Programm, das selbst keine Fehler aufweist, das jedoch fehlerhaft ist, wenn Sie es ändern, indem Sie zusammenhängende Teilzeichenfolgen mit N Zeichen entfernen
1 <= N < program length
.Zum Beispiel das dreistellige Python 2-Programm
`8`
ist ein unberührtes Programm ( danke, Sp ), da alle Programme, die aus dem Entfernen von Teilzeichenfolgen der Länge 1 resultieren, Fehler verursachen (Syntaxfehler in der Tat, aber jede Art von Fehler reicht aus):
8` `` `8
und auch alle Programme, die aus dem Entfernen von Teilzeichenfolgen der Länge 2 resultieren, verursachen Fehler:
` `
Wenn zum Beispiel
`8
ein fehlerfreies Programm gewesen`8`
wäre, wäre es nicht makellos, da alle Ergebnisse der Entfernung der Teilzeichenfolgen fehlerhaft sein müssen.Anmerkungen:
- Compiler-Warnungen gelten nicht als Fehler.
- Die fehlerhaften Unterprogramme können Eingaben oder Ausgaben annehmen oder irgendetwas anderes tun, solange sie fehlerhaft sind, egal was letztendlich passiert.
Ihre Aufgabe ist es, ein Programm mit einer Länge ungleich Null zu erstellen, das seinen eigenen Quellcode genau ausgibt und dem folgt ausgibt , Regeln für ein korrektes Quine und makellos ist.
Die kürzeste Antwort in Bytes für jede Sprache gewinnt.
Antworten:
Haskell , 132 Bytes
Probieren Sie es online!
Dies ist eine Erweiterung des Quines
show
Dies funktioniert, indem der Datenstring mit einer zitierten Version (unter Verwendung von ) von sich selbst verknüpft und das Ergebnis gedruckt wird. Dies ist jedoch nicht makellos, da alle Zeichen im Datenstring ohne Fehler entfernt werden können und auch das$(++)<*>show$
oder(++)<*>
Teil gelöscht werden kann, ohne dass das Programm unterbrochen wird.Um dies zu beheben, wird eine benutzerdefinierte Druckfunktion
q
definiert, die die Länge der angegebenen Zeichenfolge überprüft und aufruft,fail
ob sie kürzer als 132 ist. Dadurch wird das Entfernen einer beliebigen Sequenz aus der Datenzeichenfolge und auch das Entfernen von$(++)<*>show$
oder(++)<*>
, wie in beiden Fällen das Ergebnis, abgefangen Zeichenfolge, an die übergeben wird,q
ist kürzer.In
q
der Nummer132
könnte gekürzt1
werden13
,32
oder2
, aber in jedem Fall wird nochmalfail
angerufen.Soweit ich das beurteilen kann, führt das Entfernen eines anderen Teilstrings entweder zu einem Syntax- oder zu einem Typfehler, sodass das Programm überhaupt nicht kompiliert. (Haskells strenges Typensystem ist hier nützlich.)
Edit: Vielen Dank an Ørjan Johansen und Shelvacu für den Hinweis auf einen Fehler!
quelle
fail[]|length x/=122
kann entfernt werden.fail[]:[putStr x|length x==122]
könnte besser funktionieren.|length x==122
könnte dann entfernt werden.if length x==122 then putStr x else fail[]
vielleicht?if then else
vor, dachte aber, ich könnte ihn verkürzen.putStr x
Kann werdenp x
, was, als ich mein System anprobierte, sehr lange lief, bevor ich es abbrach, ich vermute, dass die Endanruf-Rekursion optimiert wurde, so dass es eine Endlosschleife ist. Ich kenne nicht genug Haskell, um Vorschläge zu machen, wie das Problem behoben werden kann.p
inq
sollte das beheben.Python 3 , 113 Bytes
Probieren Sie es online!
Wie es funktioniert
Es ist nicht einfach, mehrere Anweisungen zu verwenden, da die zweite gelöscht werden kann. Daher beginnen wir mit einem einfachen Ausdruck:
Zum Schutz vor Löschungen von Teilzeichenfolgen verwenden wir
open(1,"w").write
anstelle vonprint
. Gibt in Python 3write
die Anzahl der geschriebenen Zeichen zurück, um113
sicherzustellen, dass kein Teil der Zeichenfolge gelöscht wurde. Wir tun dies, indem wir den Rückgabewert im Dictionary{113:[]}
nachschlagen und das Ergebnis mitfor[]in…:a
durchlaufen, was fehlschlägt, wenn wir kein leeres Iterable erhalten haben oder wenn diefor
Anweisung gelöscht wird.quelle
Ruby, 78 Bytes
Ich schrieb dies, als ich über die Herausforderung nachdachte, um sicherzustellen, dass es möglich war. Es verwendet den gleichen "Wrapper" aus einer meiner Antworten auf die ursprüngliche Herausforderung verwendet.
Erläuterung:
eval(*[
ausdr])
Dies wertet den Code aus, der als Ruby-Programm zurückgegeben wird. Dies testet effektiv, dass die Zeichenfolge Code zurückgegebene ein gültiges Ruby-Programm ist. Praktischerweise können Ruby-Programme leer sein oder nur aus Leerzeichen bestehen.
Mit dem Operator "splat"
*
können Sie ein Array als Argumente für eine Funktion verwenden. Dies bedeutet auch, dass, wenneval
entfernt, das resultierende Programm(*[
expr ist])
, was kein gültiger Ruby ist.($>.write(
str)-78).chr
$>
ist eine kurze Variable für STDOUT.$>.write(foo)
schreibt foo nach STDOUT und gibt, was für diesen Code wichtig ist, die Anzahl der geschriebenen Bytes zurück.$>.write(foo)-78
: Hier78
ist die Länge des Programms, und wenn das Programm nicht entstellt ist, wird auch die Anzahl der geschriebenen Bytes angegeben. Aus diesem Grund wird im ungemischten Fall Null zurückgegeben.num.chr
Gibt num als Zeichen zurück, z. B.0.chr
wird ein String mit einem einzelnen Null-Byte zurückgegeben. In dem nicht vermischten Programm gibt dies eine Zeichenkette mit einem einzelnen Null-Byte aneval
, was ein gültiges Ruby-Programm ist, das eine No-Op ist.Im Programm kann auch eine Teilzeichenfolge entfernt werden, sodass es nur
eval(*[(78).chr])
oder isteval(*[(8).chr])
, was bedeutet, dass die numerische Konstante nicht mit einer der Zahlen (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48) enden kann , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95), weil sie ASCII-Codes für gültige Ruby-Programme mit einem Zeichen sind.%{
str}
Dies ist eine weniger bekannte Syntax für String-Literale in Ruby. Der Grund, warum es hier verwendet wird, ist, dass ausgeglichene Paare von
{}
innerhalb des Strings verwendet werden können, was bedeutet, dass diese Syntax sich selbst enthalten kann. Zum Beispiel%{foo{bar}}
ist das gleiche wie"foo{bar}"
.(s=%{
Daten})%s
Dies definiert die Variable
s
die die Daten dieses Quines sind, als printf-String.Zuweisungen in Ruby geben das zurück, was zugewiesen wurde. Dies entspricht also dem Zuweisen
s
und anschließenden Ausführens%s
%
an einer Schnur ist syntatischer Zucker für Ruby-Äquivalent von Sprintf. Das%s
bedeutet, wo in den Daten die Daten selbst eingebettet werden sollen.Dieses Codebit definiert den Datenteil des Quines und bettet ihn in sich ein, um den vollständigen Code zu erstellen.
quelle
Standard ML (MLton) ,
204182189 BytesProbieren Sie es online!
Für MLton sind vollständige SML-Programme entweder Ausdrücke, die durch
;
(z. B.print"Hello";print"World";
) begrenzt und abgeschlossen werden, oder Deklarationen mit den Schlüsselwörternvar
undfun
(z. B.var _=print"Hello"var _=print"World"
), wobei_
es sich um einen Platzhalter handelt, der auch durch einen beliebigen Variablennamen ersetzt werden kann.Die erste Option ist für makellose Programmierung nutzlos, da es sich
;
für sich genommen um ein gültiges Programm handelt (das nichts tut, aber auch keinen Fehler macht). Das Problem mit dem zweiten Ansatz ist, dass Deklarationen wievar _=print"Hello"
nurvar _="Hello"
(oder sogar gekürzt werden könnenvar _=print
) weil die Deklaration mitvar
funktioniert, solange die rechte Seite ein gültiger SML-Ausdruck oder -Wert ist (SML ist eine funktionale Sprache, also können Funktionen sein) auch als Werte verwendet).Zu diesem Zeitpunkt war ich bereit, makellose Programmierung in SML für unmöglich zu erklären, als ich zufällig auf Pattern Matching in
val
-Deklarationen stieß. Es stellt sich heraus, dass die Syntax für Deklarationen nichtval <variable_name> = <expression>
aber so istval <pattern> = <expression>
, dass ein Muster aus Variablennamen, Konstanten und Konstruktoren bestehen kann. Da derprint
Funktionstyp hatstring -> unit
, können wir eine Mustererkennung auf der Verwendungunit
-Wertes()
zu erzwingen , dass die Druckfunktion tatsächlich auf den String angewandt wird:val()=print"Hey"
. Bei diesem Ansatz führt das Entfernen von entwederprint
oder"Hey"
zu einemPattern and expression disagree
Fehler.Mit dieser Art des makellosen Druckens besteht der nächste Schritt darin, eine Quine zu schreiben, bevor schließlich weitere Sicherheitsmaßnahmen hinzugefügt werden müssen. Früher habe ich bisher eine einfache SML quine Technik (die sehen Revisionsgeschichte ), aber Anders Kaseorg wies einen anderen Ansatz aus , der einige Bytes in seinem Fall speichern. Es verwendet die eingebaute
String.toString
Funktion, um das Entziehen von Zeichenfolgen zu handhaben, und hat die allgemeine Form<code>"<data>"
, wobei"<data>"
es sich um eine entkoppelte Zeichenfolge dercode
Vorgängerversion handelt:Dies ist eine funktionierende Quine, aber noch nicht makellos. Zunächst stellte Anders Kaseorg fest, dass MLton ein einfaches Anführungszeichen
"
als Code akzeptiert, ohne Fehler zu erzeugen. Dies bedeutet, dass der Code nicht wie oben in einem Anführungszeichen endet. Der kürzeste Weg, dies zu verhindern, wäre, alles nachherval()=
in Klammern zu setzen, aber dann könnte der Code auf reduziert werdenval()=()
. Der zweitkürzeste Weg, den ich gefunden habe, besteht darinval()=hd[ ... ]
, alles in eine Liste zu packen und das erste Element zurückzugeben, um den Typprüfer glücklich zu machen.Damit kein Teil des Datenstrings unbemerkt entfernt werden kann, bietet sich die Mustererkennung in
val
-declarations wieder an: Die Länge des endgültig zu druckenden Strings (und damit die Programmlänge) sollte also 195 betragen wir können stattdessenlet val t=... val 195=size t in print t end
in den Körper derfn
Abstraktion schreibenprint(...)
. Das Entfernen eines Teils der Zeichenfolge führt zu einer Länge von weniger als 189, wodurch eineBind
Ausnahme ausgelöst wird.Es gibt noch ein Problem: Der gesamte
val 195=size t
Scheck könnte einfach fallengelassen werden. Wir können dies verhindern, indem wir das Häkchen so erweitern, dass es mit einem Tupel übereinstimmt :val t=... val(216,u)=(n+size t,t)in print u end
, sodass das Entfernen des Häkchens zu einer ungebundenen Variablen führtu
.Insgesamt ergibt dies die folgende 195-Byte-Lösung:
Die Anwendung des Golf - Tricks mit Operator Variablennamen wie
!
,$
und%
stattn
,t
undu
um etwas Platz zu sparen (siehe diesen Tipp ) führt zur endgültigen 182 Byte - Version.Alle anderen Teilzeichenfolgenentfernungen, die in der Erläuterung nicht explizit angegeben wurden, sollten zu einem Syntax- oder Typfehler führen.
Edit 1:
length(explode t)
ist geradesize t
.Edit 2: Vielen Dank an Anders Kaseorg für einen anderen Ansatz und den Hinweis auf eine "Sicherheitslücke".
quelle
"\""
direktes Schreiben und VerwendenString.toString
für das Escaping."
und erzeugt eine leere Ausgabe ( TIO ).let ... in ... end
."
wie MLton als Programm akzeptiert wird , und es scheint, dass der Fehler in diesem Commit behoben wurde. Vielleicht sind Ihre 182 oder meine 180 in Ordnung, solange Sie die unveröffentlichte Git-Version von MLton angeben.