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 var
et 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 var
fonctionne 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 val
dé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 print
fonction 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 print
ou de l' autre "Hey"
entraîne une Pattern and expression disagree
erreur.
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.toString
fonction 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' code
avant:
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 val
modè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 end
dans le corps de l' fn
abstraction au lieu de print(...)
. La suppression d'une partie de la chaîne entraîne une longueur inférieure à 189, provoquant ainsi la Bind
levée d' une exception.
Il reste un problème: tout le val 195=size t
chè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 end
sorte 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
, t
et u
afin 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é".