Quining un mundo prístino

16

Este desafío se basa en la pregunta de Helka Homba Programación de un mundo prístino . A partir de esa pregunta, la definición de un programa original es:

Definamos un programa prístino como un programa que no tiene ningún error en sí mismo, pero lo hará si lo modifica quitando cualquier subcadena contigua de N caracteres, donde 1 <= N < program length.

Por ejemplo, el programa Python 2 de tres caracteres

`8`

es un programa impecable ( gracias, Sp ) porque todos los programas resultantes de la eliminación de subcadenas de longitud 1 causan errores (errores de sintaxis, de hecho, pero cualquier tipo de error funcionará):

8`
``
`8

y también todos los programas resultantes de eliminar subcadenas de longitud 2 causan errores:

`
`

Si, por ejemplo, `8hubiera sido un programa sin errores, entonces `8`no sería perfecto porque todos los resultados de la eliminación de la subcadena deben ser erróneos.

Notas:

  • Las advertencias del compilador no cuentan como errores.
  • Los subprogramas de error pueden tomar entrada o dar salida o hacer cualquier otra cosa, siempre y cuando se produzca un error sin importar lo que pase.

Su tarea es crear un programa de longitud distinta de cero que imprima su propio código fuente exactamente, siga las reglas para una quine adecuada y sea impecable.

La respuesta más corta en bytes para cada idioma gana.

Shelvacu
fuente
¿Supongo que los idiomas que no tienen errores no pueden competir?
ATaco
@ATaco Lamentablemente sí. Otros lenguajes, como lisp, tienen la sintaxis estructurada de tal manera que es imposible hacer un programa prístino útil.
Shelvacu
RIP en realidad / en serio
ATaco
'La respuesta más corta en bytes para cada idioma gana'. No estoy seguro de que Short sea la mejor medida para un programa inmaculado.
P. Siehr
@ P.Siehr ¿Qué recomendarías en su lugar?
Shelvacu

Respuestas:

6

Haskell , 132 bytes

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$"

Pruébalo en línea!

Esta es una extensión de la quine

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

que funciona concatenando la cadena de datos con una versión citada (usando show) de sí misma e imprimiendo el resultado. Sin embargo, esto no es perfecto, ya que cualquier carácter en la cadena de datos puede eliminarse sin fallar y también la parte $(++)<*>show$o (++)<*>podría eliminarse sin que se rompa el programa.

Para solucionar esto, una función de impresión personalizada q se define que verifica la longitud de la cadena dada y llama failsi es más corta que 132. Esto detecta la eliminación de cualquier secuencia de la cadena de datos y también la eliminación de $(++)<*>show$o(++)<*> , como en ambos casos el resultado la cadena pasada a qes más corta.

En qel número 132podría acortarse a1 , 13, 32o 2, pero en cada caso de nuevofail que se llama.

Por lo que puedo decir, la eliminación de cualquier otra subcadena causa un error de sintaxis o de tipo, por lo que el programa ni siquiera se compila en primer lugar. (El estricto sistema de tipos de Haskell es útil aquí).

Editar: Gracias a Ørjan Johansen y Shelvacu por señalar fallas!

Laikoni
fuente
Me temo que fail[]|length x/=122se puede eliminar. fail[]:[putStr x|length x==122]Podría funcionar mejor.
Ørjan Johansen
Argh, no, entonces |length x==122podría ser eliminado. if length x==122 then putStr x else fail[]¿quizás?
Ørjan Johansen
@ ØrjanJohansen Buena captura, tuve un if then elseantes pero pensé que podría acortarlo.
Laikoni
2
putStr xpuede llegar a ser p x, lo que cuando probé en mi sistema funcionó durante mucho tiempo antes de matarlo, sospecho que la recursión de la cola se optimizó, por lo que es un bucle infinito. No conozco suficiente haskell para dar sugerencias sobre cómo solucionarlo.
Shelvacu
@Shelvacu Whoops. Cambiar el nombre pa qdebería solucionar eso.
Ørjan Johansen
4

Python 3 , 113 bytes

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

Pruébalo en línea!

Cómo funciona

No podemos usar fácilmente varias declaraciones ya que la segunda podría eliminarse, por lo que comenzamos con un quine de expresión única:

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

Para protegerlo contra las eliminaciones de subcadenas, utilizamos en open(1,"w").writelugar de print. En Python 3,write devuelve el número de caracteres escritos, lo que verificaremos es 113asegurarnos de que no se eliminó ninguna parte de la cadena. Hacemos esto al buscar el valor de retorno en el diccionario {113:[]}y recorrer el resultado con for[]in…:a, que fallará si no obtuvimos un iterable vacío o si la fordeclaración se elimina.

Anders Kaseorg
fuente
1
¿Podría dar una explicación de cómo funciona su código?
Shelvacu
@Shelvacu Sí, agregó.
Anders Kaseorg
3

Ruby, 78 bytes

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

Escribí esto cuando pensé en el desafío para asegurarme de que fuera posible. Utiliza el mismo "contenedor" de una de mis respuestas al desafío original.

Explicación:

  • eval(*[ expr ])

    Esto evalúa cualquier código devuelto como un programa ruby. Esto efectivamente prueba que la cadena que código devuelve el es un programa ruby ​​válido. Convenientemente, los programas ruby ​​pueden estar en blanco o solo consisten en espacios en blanco.

    El operador "splat" le *permite usar una matriz como argumentos para una función. Esto también significa que si evalse elimina, el programa resultante es (*[ expr ]) , que no es válido ruby.

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

    $> es una variable corta para STDOUT.

    $>.write(foo) escribe foo en STDOUT y, lo que es importante para este código, devuelve el número de bytes escritos.

    $>.write(foo)-78: Aquí 78está la duración del programa, y ​​si el programa no está maltratado, también será el número de bytes escritos. Por lo tanto, en el caso no mutilado, esto devolverá cero.

    num.chrdevuelve num como un carácter, por ejemplo 0.chr, devolverá una cadena que contiene un solo byte nulo. En el programa no desencadenado, esto dará una cadena con un solo byte nulo eval, que es un programa ruby ​​válido que no funciona.

    Además, el programa puede eliminar una subcadena de modo que sea justo eval(*[(78).chr])oeval(*[(8).chr]) , lo que significa que la constante numérica no puede terminar con ninguno de los números (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48 , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95) porque son códigos ASCII para programas válidos de rubí de un solo carácter.

  • %{ str }

    Esta es una sintaxis menos conocida para los literales de cadena en ruby. La razón por la que se usa aquí es que {}se pueden usar pares balanceados dentro de la cadena, lo que significa que esta sintaxis puede contenerse. Por ejemplo, %{foo{bar}}es lo mismo que "foo{bar}".

  • (s=%{ datos })%s

    Esto define la variable s que son los datos de esta línea, como una cadena printf.

    Las asignaciones en ruby ​​devuelven lo asignado, por lo que es lo mismo que la primera asignación s y luego ejecutars%s

    %en una cuerda hay azúcar sintético para el equivalente de ruby ​​de sprintf. los%s significa en donde dentro de los datos de los propios datos deben ser embebidos.

    Este bit de código define la porción de datos del quine y lo integra dentro de sí mismo para crear el código completo.

Shelvacu
fuente
3

ML estándar (MLton) , 204 182 189 bytes

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".

Laikoni
fuente
−2 bytes escribiendo "\""directamente y usando String.toStringpara escapar.
Anders Kaseorg
Espera, esto es horrible: MLton parece aceptar el programa ", produciendo una salida vacía ( TIO ).
Anders Kaseorg el
@ AndersKaseorg Huh, eso es extraño. Sin embargo, debería ser posible solucionar este problema utilizando otro let ... in ... end.
Laikoni
@ AndersKaseorg Solucioné el problema, con suerte sin introducir nuevas "vulnerabilidades".
Laikoni
En realidad, investigué si MLton aceptaba "como programa, y ​​parece que se corrigió el error en esta confirmación , por lo que tal vez su 182 o mi 180 esté bien siempre que especifique la versión Git inédita de MLton.
Anders Kaseorg el