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 vary 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 valdeclaraciones. 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 printfunción tiene el tipo string -> unit, podemos usar una comparación de patrones en el unitvalor 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 printo "Hey"resulta en un Pattern and expression disagreeerror.
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.toStringfunción incorporada para manejar el escape de cadenas y es de forma general <code>"<data>", donde "<data>"es una cadena escapada de codeantes:
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 valpatrones 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 enden el cuerpo de la fnabstracció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 Bindexcepción.
Todavía queda un problema: toda la val 195=size tverificació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 endmodo 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, ty upara 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".