Quining un monde vierge

16

Ce défi est basé sur des Helka Homba question de la programmation d' une Pristine mondiale . De cette question, la définition d'un programme vierge est:

Définissons un programme vierge comme un programme qui ne contient aucune erreur lui-même mais qui provoquera une erreur si vous le modifiez en supprimant toute sous-chaîne contiguë de N caractères, où 1 <= N < program length.

Par exemple, le programme Python 2 à trois caractères

`8`

est un programme vierge ( merci, Sp ) car tous les programmes résultant de la suppression de sous-chaînes de longueur 1 provoquent des erreurs (des erreurs de syntaxe en fait, mais n'importe quel type d'erreur fera l'affaire):

8`
``
`8

et aussi tous les programmes résultant de la suppression de sous-chaînes de longueur 2 provoquent des erreurs:

`
`

Si, par exemple, `8avait été un programme sans erreur, il `8`ne serait pas vierge car tous les résultats de la suppression de la sous-chaîne doivent être erronés.

Remarques:

  • Les avertissements du compilateur ne comptent pas comme des erreurs.
  • Les sous-programmes en erreur peuvent prendre une entrée ou donner une sortie ou faire autre chose tant qu'ils font une erreur, peu importe ce qui finit par se produire.

Votre tâche consiste à créer un programme de longueur non nulle qui imprime exactement son propre code source, suit les règles d'un quine correct et est vierge.

La réponse la plus courte en octets pour chaque langue gagne.

Shelvacu
la source
Je suppose que les langues qui ne font pas d'erreur ne peuvent pas rivaliser?
ATaco
@ATaco Malheureusement oui. D'autres langages, comme lisp, ont une syntaxe structurée de telle manière qu'il est impossible de créer un programme vierge utile.
Shelvacu
RIP en fait / sérieusement
ATaco
«La réponse la plus courte en octets pour chaque langue gagne.» Je ne suis pas sûr que le short soit la meilleure mesure pour un programme vierge.
P. Siehr
@ P.Siehr Que recommanderiez-vous à la place?
Shelvacu

Réponses:

6

Haskell , 132 octets

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

Essayez-le en ligne!

Ceci est une extension du quine

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

qui fonctionne en concaténant la chaîne de données avec une version citée (en utilisant show) d'elle-même et en imprimant le résultat. Cependant, ce n'est pas vierge car tous les caractères de la chaîne de données peuvent être supprimés sans échec et la partie $(++)<*>show$ou (++)<*>peut également être supprimée sans interruption du programme.

Pour résoudre ce problème, une fonction d'impression personnalisée qest définie qui vérifie la longueur de la chaîne donnée et appelle failsi elle est inférieure à 132. Cela intercepte la suppression de toute séquence de la chaîne de données et également les suppressions de $(++)<*>show$ou (++)<*>, comme dans les deux cas, le résultat la chaîne passée à qest plus courte.

Dans qle nombre 132pourrait être réduit à 1, 13, 32ou 2, mais encore une fois dans chaque cas failest appelé.

Pour autant que je sache, la suppression de toute autre sous-chaîne provoque une erreur de syntaxe ou de type, de sorte que le programme ne compile même pas en premier lieu. (Le système de type strict de Haskell est utile ici.)

Edit: Merci à Ørjan Johansen et Shelvacu pour avoir signalé un défaut!

Laikoni
la source
J'ai peur fail[]|length x/=122qu'on puisse l'enlever. fail[]:[putStr x|length x==122]pourrait mieux fonctionner.
Ørjan Johansen
Argh, non, alors |length x==122pourrait être retiré. if length x==122 then putStr x else fail[]peut-être?
Ørjan Johansen
@ ØrjanJohansen Bonne prise, j'ai eu un if then elseavant mais j'ai pensé que je pouvais le raccourcir.
Laikoni
2
putStr xpeut devenir p x, ce qui, lorsque j'ai essayé mon système, a fonctionné très longtemps avant de le tuer, je soupçonne que la récursivité des appels de queue a été optimisée, c'est donc une boucle infinie. Je ne connais pas suffisamment de haskell pour donner des suggestions sur la façon de le réparer.
Shelvacu
@Shelvacu Whoops. Renommer pen qdevrait résoudre ce problème.
Ørjan Johansen
4

Python 3 , 113 octets

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

Essayez-le en ligne!

Comment ça fonctionne

Nous ne pouvons pas facilement utiliser plusieurs instructions car la seconde pourrait être supprimée, nous commençons donc par une quine à expression unique:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

Pour le protéger contre les suppressions de sous-chaîne, nous utilisons à la open(1,"w").writeplace de print. En Python 3, writeretourne le nombre de caractères écrits, que nous vérifierons 113pour nous assurer qu'aucune partie de la chaîne n'a été supprimée. Nous faisons cela en recherchant la valeur de retour dans le dictionnaire {113:[]}et en parcourant le résultat avec for[]in…:a, qui échouera si nous n'avons pas obtenu d'itérable vide ou si l' forinstruction est supprimée.

Anders Kaseorg
la source
1
Pourriez-vous expliquer comment fonctionne votre code?
Shelvacu
@Shelvacu Ouais, a ajouté.
Anders Kaseorg
3

Rubis, 78 octets

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

J'ai écrit cela quand j'ai pensé au défi pour m'assurer que c'était possible. Il utilise le même "wrapper" de l' une de mes réponses au défi original.

Explication:

  • eval(*[ expr ])

    Cela évalue tout code retourné en tant que programme ruby. Cela teste efficacement que la chaîne renvoyée par le code est un programme ruby ​​valide. De manière pratique, les programmes rubis peuvent être vides ou uniquement constitués d'espaces.

    L'opérateur "splat" *vous permet d'utiliser un tableau comme arguments d'une fonction. Cela signifie également que si evalest supprimé, le programme résultant est (*[ expr ]) , ce qui n'est pas un rubis valide.

  • ($>.write( str )-78).chr

    $> est une variable courte pour STDOUT.

    $>.write(foo) écrit foo dans STDOUT et, surtout pour ce code, retourne le nombre d'octets écrits.

    $>.write(foo)-78: Voici 78la longueur du programme, et donc si le programme n'est pas modifié, sera également le nombre d'octets écrits. Par conséquent, dans le cas démêlé, cela retournera zéro.

    num.chrrenvoie num sous forme de caractère, par exemple 0.chr, renverra une chaîne contenant un seul octet nul. Dans le programme démêlé, cela donnera une chaîne avec un seul octet nul à eval, qui est un programme ruby ​​valide qui est un no-op.

    En outre, le programme peut avoir une sous-chaîne supprimée de sorte qu'elle soit juste eval(*[(78).chr])ou eval(*[(8).chr]), ce qui signifie que la constante numérique ne peut se terminer par aucun des nombres (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48 , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95) car ce sont des codes ascii pour des programmes rubis à un seul caractère valides.

  • %{ str }

    Il s'agit d'une syntaxe moins connue pour les littéraux de chaîne en ruby. La raison pour laquelle il est utilisé ici est que des paires équilibrées de {}peuvent être utilisées dans la chaîne, ce qui signifie que cette syntaxe peut se contenir. Par exemple, %{foo{bar}}est identique à "foo{bar}".

  • (s=%{ Les données })%s

    Ceci définit la variable squi est les données de ce quine, comme une chaîne printf.

    Les affectations en rubis renvoient ce qui a été attribué, c'est donc la même chose que la première affectation spuis l'exécutions%s

    %sur une chaîne est du sucre syntaxique pour l'équivalent rubis de sprintf. Le %ssignifie où, dans les données, les données elles-mêmes doivent être intégrées.

    Ce bit de code définit la partie données du quine et l'intègre en lui-même pour créer le code complet.

Shelvacu
la source
3

Standard ML (MLton) , 204 182 189 octets

val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]

Essayez-le en ligne!

Pour MLton, les programmes SML complets sont des expressions délimitées et terminées par ;( par exemple print"Hello";print"World";) ou des déclarations avec les mots clés varet fun(par exemple var _=print"Hello"var _=print"World") où _est un caractère générique qui pourrait également être remplacé par n'importe quel nom de variable.

La première option est inutile pour une programmation vierge, car ;en elle-même est un programme valide (qui ne fait rien, mais ne fait pas d'erreur non plus). Le problème avec la deuxième approche est que les déclarations comme var _=print"Hello"peuvent être raccourcies à juste var _="Hello"(ou même var _=print) parce que la déclaration avec varfonctionne tant que le côté droit est une expression ou une valeur SML valide (SML est un langage fonctionnel, donc les fonctions peuvent être utilisé comme valeur aussi).

À ce stade, j'étais prêt à déclarer impossible une programmation vierge en SML, quand par hasard je suis tombé sur une correspondance de modèle dans les valdéclarations. Il s'avère que la syntaxe des déclarations n'est pas val <variable_name> = <expression>mais val <pattern> = <expression>, où un modèle peut être composé de noms de variables, de constantes et de constructeurs. Comme la printfonction est de type string -> unit, nous pouvons utiliser un match de motif sur la unit-valeur ()pour faire respecter que la fonction d'impression est en fait appliquée à la chaîne: val()=print"Hey". Avec cette approche, la suppression de l'un printou de l' autre "Hey"entraîne une Pattern and expression disagreeerreur.

Avec cette façon d'imprimer à portée de main, la prochaine étape consiste à écrire une quine, avant enfin de rajouter de la sauvegarde. J'ai déjà utilisé une technique de quine SML simple (voir l' historique des révisions ), mais Anders Kaseorg a souligné une approche différente qui peut économiser quelques octets dans son cas. Il utilise la String.toStringfonction intégrée pour gérer l'échappement de chaîne et est de la forme générale <code>"<data>", où se "<data>"trouve une chaîne d'échappement de l' codeavant:

val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"

Il s'agit d'un quine de travail mais pas encore vierge. Tout d'abord, Anders Kaseorg a découvert que MLton accepte une seule citation "comme code sans produire d'erreurs, ce qui signifie que nous ne pouvons pas avoir de code se terminant par une citation comme ci-dessus. Le moyen le plus court d'empêcher cela serait de tout envelopper val()=dans une paire de parenthèses, mais le code pourrait alors être réduit à val()=(). La deuxième façon la plus courte que j'ai trouvée est d'utiliser val()=hd[ ... ], c'est-à-dire que nous emballons tout dans une liste et retournons son premier élément pour rendre le vérificateur de type heureux.

Pour vous assurer qu'aucune partie de la chaîne de données ne peut être supprimée sans être remarquée, la correspondance de valmodèle dans les déclarations est à nouveau utile: la longueur de la chaîne finale à imprimer (et donc la longueur du programme) doit être égale à 195, donc nous pouvons écrire let val t=... val 195=size t in print t enddans le corps de l' fnabstraction au lieu de print(...). La suppression d'une partie de la chaîne entraîne une longueur inférieure à 189, provoquant ainsi la Bindlevée d' une exception.

Il reste un problème: tout le val 195=size tchèque pourrait simplement être abandonné. Nous pouvons éviter cela en développant la vérification pour qu'elle corresponde à un tuple:, de val t=... val(216,u)=(n+size t,t)in print u endsorte que la suppression de la vérification entraîne une variable non liée u.

Au total, cela donne la solution de 195 octets suivante:

val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]

L'application de l'astuce de golf consistant à utiliser des noms de variables d'opérateur comme !, $et %au lieu de n, tet uafin d'économiser de l'espace blanc (voir cette astuce ) conduit à la version finale de 182 octets.

Toutes les autres suppressions de sous-chaînes qui, lorsqu'elles ne sont pas explicitement indiquées dans l'explication, devraient entraîner une erreur de syntaxe ou de type.

Edit 1: length(explode t) est juste size t.
Edit 2: Merci à Anders Kaseorg pour une approche de quine différente et en soulignant une "vulnérabilité".

Laikoni
la source
−2 octets en écrivant "\""directement et en utilisant String.toStringpour l'échappement.
Anders Kaseorg
Attendez, c'est horrible: MLton semble accepter le programme ", produisant une sortie vide ( TIO ).
Anders Kaseorg
@AndersKaseorg Huh, c'est étrange. Cependant, il devrait être possible de résoudre ce problème en utilisant un autre let ... in ... end.
Laikoni
@AndersKaseorg J'ai résolu le problème, j'espère sans introduire de nouvelles "vulnérabilités".
Laikoni
En fait, je me suis penché sur l'acceptation de MLton en "tant que programme, et il semble que le bogue a été corrigé dans ce commit , alors peut-être que votre 182 ou mon 180 est correct tant que vous spécifiez la version Git non publiée de MLton.
Anders Kaseorg