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)"]
Pruébalo en línea!
Para MLton, los programas SML completos son expresiones delimitadas y terminadas por ;
( por ejemplo print"Hello";print"World";
) o declaraciones con las palabras clave var
y fun
(por ejemplo var _=print"Hello"var _=print"World"
) donde_
hay un comodín que también podría reemplazarse por cualquier nombre de variable.
La primera opción es inútil para la programación inmaculada porque ;
por sí sola es un programa válido (que no hace nada, pero tampoco falla). El problema con el segundo enfoque es que las declaraciones como var _=print"Hello"
se pueden acortar a solo var _="Hello"
(o incluso var _=print
) porque la declaración convar
funciona siempre que el lado derecho sea una expresión o valor SML válido (SML es un lenguaje funcional, por lo que las funciones pueden ser usado como valores también).
En este punto, estaba listo para declarar imposible la programación prístina en SML, cuando por casualidad me topé con la coincidencia de patrones en las val
declaraciones. Resulta que la sintaxis para las declaraciones no es val <variable_name> = <expression>
sino val <pattern> = <expression>
, donde un patrón puede consistir en nombres de variables, constantes y constructores. A medida que la print
función tiene el tipo string -> unit
, podemos usar una comparación de patrones en el unit
valor P ()
para hacer cumplir la función de impresión que se aplica realmente a la cadena: val()=print"Hey"
. Con este enfoque, eliminar cualquiera de los dos print
o "Hey"
resulta en un Pattern and expression disagree
error.
Con esta forma de impresión impecable a mano, el siguiente paso es escribir un quine, antes de que finalmente se necesite agregar más protección de salvamento. Anteriormente utilicé una técnica de quine SML fácil (consulte el historial de revisiones ), pero Anders Kaseorg señaló un enfoque diferente que puede ahorrar algunos bytes en su caso. Utiliza la String.toString
función incorporada para manejar el escape de cadenas y es de forma general <code>"<data>"
, donde "<data>"
es una cadena escapada de code
antes:
val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"
Esta es una quine de trabajo pero aún no está impecable. En primer lugar, Anders Kaseorg descubrió que MLton acepta una sola cita "
como código sin generar errores, lo que significa que no podemos tener un código que termine en una cita como se indicó anteriormente. La forma más corta de evitar esto sería envolver todo después val()=
en un paréntesis, sin embargo, el código podría reducirse a val()=()
. La segunda forma más corta que encontré es usar val()=hd[ ... ]
, es decir, envolvemos todo en una lista y devolvemos su primer elemento para hacer feliz el verificador de tipos.
Para asegurarse de que no se pueda eliminar ninguna parte de la cadena de datos sin que se note, la coincidencia de val
patrones en las declaraciones vuelve a ser útil: la longitud de la cadena final a imprimir (y, por lo tanto, la longitud del programa) debe ser igual a 195, por lo que podemos escribir let val t=... val 195=size t in print t end
en el cuerpo de la fn
abstracción en lugar de print(...)
. La eliminación de una parte de la cadena da como resultado una longitud inferior a 189, lo que provoca una Bind
excepción.
Todavía queda un problema: toda la val 195=size t
verificación simplemente podría descartarse. Podemos evitar esto expandiendo la verificación para que coincida con una tupla: de val t=... val(216,u)=(n+size t,t)in print u end
modo que al eliminar la verificación se obtiene una variable independiente u
.
En total, esto produce la siguiente solución de 195 bytes:
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)"]
La aplicación del truco de golf de usar nombres de variables de operador como !
, $
y en %
lugar de n
, t
y u
para ahorrar algo de espacio en blanco (ver este consejo ) conduce a la versión final de 182 bytes.
Todas las demás eliminaciones de subcadenas que no se indiquen explícitamente en la explicación deberían dar lugar a un error de tipo o sintaxis.
Edición 1: length(explode t)
es justo size t
.
Edición 2: Gracias a Anders Kaseorg por un enfoque de quine diferente y señalando una "vulnerabilidad".