Generador de quinas generalizado

19

El reto

En este desafío, especifica un idioma de origen S y un idioma de destino T . Su tarea es escribir el siguiente programa Pen el idioma S. Si se proporciona un programa válido Qen el idioma Tcomo entrada P, generará un programa válido Ren el idioma Tque no recibe entradas y salidas Q(R), es decir, el programa Qaplicado al código fuente de R. Además , debe presentar en su respuesta un programa de ejemplo no trivial Q(cuanto más interesante, mejor, aunque no obtenga puntos por esto), el programa resultante Ry la salida de R. Este es el código de golf, por lo que el código más corto para Pvictorias.

En otras palabras, este es un desafío sobre escribir un "constructor universal de quines" que pueda crear tipos arbitrarios de quines generalizadas.

Aclaraciones

  • Sus idiomas de origen y destino pueden ser idénticos.
  • El programa Pdebe tomar una cadena como entrada (desde STDIN o equivalente), y generar una cadena (hacia STDOUT o equivalente), como debería hacerlo cada programa de salida R.
  • Los programas de entrada Qtambién deben transformar una cadena en otra cadena, pero su forma es más flexible: pueden ser funciones de cadena a cadena, fragmentos de código que modifican una variable con un nombre determinado, fragmentos que modifican la pila de datos si su idioma de destino tiene uno, etc. También puede restringir aún más la forma de los Q's indicando que, por ejemplo, no pueden contener ningún comentario. Sin embargo, debe ser capaz de implementar cualquier función de cadena a cadena computable como un programa de entrada Q, y debe indicar explícitamente cómo funcionan y qué restricciones adicionales les impone.
  • El programa de salida Rrealmente debería ser una quine (generalizada), por lo que no debe leer ninguna entrada (entrada del usuario, archivos, etc.) a menos Qque lo haga.
  • Las lagunas estándar no están permitidas.

Un ejemplo

Supongamos que elijo Python como mi idioma de origen y Haskell como mi idioma de destino, y además necesito que el programa de entrada sea una definición de una línea de una String -> Stringfunción llamada f. Si doy el programa de inversión de cadenas

f x = reverse x

como entrada a mi programa Python P, generará el código fuente de otro programa Haskell R. Este programa imprime en STDOUT el código fuente de R, pero se invierte. Si Pse le da la función de identidad

f x = x

Como entrada, el programa de salida Res una quine.

Zgarb
fuente

Respuestas:

7

Fuente = Destino = CJam, 19 17 16 bytes

{`"_~"+}`)q\"_~"

Esto supone que el programa de entrada Q(dado en STDIN) es un fragmento de CJam que espera una cadena en la parte superior de la pila y deja otra cadena en la parte superior de la pila.

Pruébalo aquí.

Ejemplos

  1. La identidad solo sería un fragmento vacío, por lo que dejaría STDIN impresiones vacías

    {`"_~"+}_~
    

    Cuál es la quine estándar, con un adicional +.

  2. Para revertir una cadena en CJam, puede usar W%, por lo que poner eso en STDIN, esto produce:

    {`"_~"+W%}_~
    

    que podemos correr para obtener

    ~_}%W+"~_"`{
    
  3. Como tercer ejemplo, decimos que utilizamos un fragmento, que intercala una cadena con espacios: ' *. Corriendo Pcon eso como entrada, obtenemos

    {`"_~"+' *}_~
    

    que a su vez imprime

    { ` " _ ~ " + '   * } _ ~  
    
  4. Ahora también funciona si Qcontiene saltos de línea (aunque eso nunca es necesario en CJam). Aquí hay un programa con un salto de línea, que elimina todos los saltos de línea de una cadena (de una manera innecesariamente complicada: se divide en líneas y luego se une):

    N/
    ""
    *
    

    Esto da como resultado lo siguiente R:

    {`"_~"+N/
    ""
    *}_~
    

    que a su vez imprime

    {`"_~"+N/""*}_~
    

Explicación

Veamos primero la salida producida:

La quine estándar de CJam es

{`"_~"}_~

Funciona de la siguiente manera:

  • Empuja el bloque {`"_~"}.
  • Duplícalo con _.
  • Ejecute la copia con ~.
  • Ahora dentro del bloque, ` convierte el primer bloque en su representación de cadena.
  • "_~" empuja los dos caracteres de la fuente que no forman parte del bloque (y, por lo tanto, faltan en la representación de la cadena).
  • Las dos cadenas se imprimen consecutivamente al final del programa.

En el quine básico, `es innecesario, porque si simplemente dejas el bloque como está, se imprime al final del programa.

El resultado de mi programa Pes una versión modificada de este fragmento. Primero, agregué una +al bloque, que concatena las dos cadenas en una cadena que contiene toda la fuente. Tenga en cuenta que esto será cierto sin importar lo que haga dentro del bloque, porque todo eso se agregará a la representación de cadena obtenida con `. Ahora simplemente puedo poner el programa / fragmento Qdentro del bloque después del +, para que pueda modificar la cadena de origen antes de que se imprima. De nuevo, desdeQ va dentro del bloque, será parte de dicha cadena fuente.

En resumen, Pimpresiones

{`"_~"+Q}_~

Ahora, para ver cómo construyo esta salida en P:

{`"_~"+}         "Push the block without Q.";
        `        "Turn it into a string. This is shorter than writing a string right away,
                  because I'd have to escape the quotes, and I'd need two quotes instead of
                  one backtick.";
         )       "Pop off the last character (the brace) and push it on the stack.";
          q      "Read input Q.";
           \     "Swap Q with the brace.";
            "_~" "Push the final two characters.";

Las cuatro cadenas se imprimen automáticamente (consecutivas) al final del programa.

Martin Ender
fuente
1
Bueno, esto fue rápido! Y ciertamente difícil de superar. La explicación también es buena.
Zgarb
¿Dónde aprendiste que el W% se invierte? dl.dropboxusercontent.com/u/15495351/cjam.pdf no lo tiene
Faraz Masroor
¿Hay una lista más completa de métodos?
Faraz Masroor
@FarazMasroor sourceforge.net/p/cjam/wiki/Basic%20operators/#percent (no. 3) ... es una característica prestada de GolfScript y en algún momento alguien me dijo que así es como funciona en GolfScript. Parece ser un idioma tan común que es un conocimiento implícito extraño que todo usuario de CJam / GS tiene pero que en realidad no se explica en muchos lugares. (Para obtener más información, operadores no bien documentados, consulte sourceforge.net/p/cjam/wiki/Operators )
Martin Ender
3

Haskell expresiones → Haskell expresiones, 41 bytes

((++)<*>show).('(':).(++")$(++)<*>show$")

Pruébalo en línea!

Cómo funciona

P $ "Q"= ((++)<*>show).('(':).(++")$(++)<*>show$") $ "Q"construcciones "R"por

  1. (++")$(++)<*>show$"): agregando la cadena ")$(++)<*>show$" ,
  2. ('(':): anteponer el personaje '(' , y
  3. (++)<*>show(= \x->x++show x): anexando una versión citada de eso,

resultando en "R"= "(Q)$(++)<*>show$\"(Q)$(++)<*>show$\"".

R= (Q)$(++)<*>show$"(Q)$(++)<*>show$"trabaja por

  1. teniendo la cadena "(Q)$(++)<*>show$",
  2. (++)<*>show: agregando una versión citada de eso,
  3. aplicando Qa eso,

resultando en Q "(Q)$(++)<*>show$\"(Q)$(++)<*>show$\""= Q "R".

(Los parens alrededor Qson necesarios porque Qpueden contener $tan fácilmente como lo Rhace, y $desafortunadamente son correctos asociativos).

Manifestación

λ> putStrLn $ ((++)<*>show).('(':).(++")$(++)<*>show$") $ "id"
(id)$(++)<*>show$"(id)$(++)<*>show$"
λ> putStrLn $ (id)$(++)<*>show$"(id)$(++)<*>show$"
(id)$(++)<*>show$"(id)$(++)<*>show$"
λ> putStrLn $ ((++)<*>show).('(':).(++")$(++)<*>show$") $ "reverse"
(reverse)$(++)<*>show$"(reverse)$(++)<*>show$"
λ> putStrLn $ (reverse)$(++)<*>show$"(reverse)$(++)<*>show$"
"$wohs>*<)++($)esrever("$wohs>*<)++($)esrever(
λ> putStrLn $ ((++)<*>show).('(':).(++")$(++)<*>show$") $ "length"
(length)$(++)<*>show$"(length)$(++)<*>show$"
λ> print $ (length)$(++)<*>show$"(length)$(++)<*>show$"
44
Anders Kaseorg
fuente
No sólo $necesita los paréntesis, pero también se arrastra let, doo expresiones lambda.
Ørjan Johansen
@ ØrjanJohansen Correcto, pero podría haber definido un subconjunto de idiomas que no permite lambda sin paréntesis / let/ if/ case/ dosi no los emito yo mismo. Quizás sea mejor que no tuviera que hacerlo.
Anders Kaseorg
2

Fuente = Destino = JavaScript, 66

console.log("function a(){console.log("+prompt()+"(a+'a()'))}a()")

Suposiciones para Q:

  • Q debe ser una función anónima JavaScript de cadena a cadena.

Ejemplos:

  • Reverso . Q =function(s) { return s.split('').reverse().join(''); }

En este caso, P(Q)(o R) será:, function a(){console.log(function(s) { return s.split('').reverse().join(''); }(a+'a()'))}a()y al ejecutarlo, obtendremos: )(a}))')(a'+a(} ;)''(nioj.)(esrever.)''(tilps.s nruter { )s(noitcnuf(gol.elosnoc{)(a noitcnufque es exactamente lo mismo que Q(R).

  • Identidad . Q =function(s) { return s; }

en este caso, P(Q)(o R) será: function a(){console.log(function(s) { return s; }(a+'a()'))}a()que es una Quine de JavaScript . No hace falta decir Q(R)que será lo mismo, ya que Q es la función de identidad.


Algunas notas:

STDIN en JavaScript es tradicionalmente prompt(), sin embargo, me permití abstenerme de la tradición de alert()STDOUT, para facilitar el proceso de ejecución de salida como un programa utilizando copiar y pegar. (Me doy cuenta de que puedo guardar hasta 12 caracteres al cambiar a alert()).

También puedo hacer las cosas mucho más cortas en ES6, pero quiero quedarme con JavaScript nativo por ahora. Estoy considerando enviar una respuesta S = Scala, T = ECMA6 en el futuro, solo por la experiencia.

También me doy cuenta de que JavaScript casi nunca puede vencer a CJam en , ¡pero tuve que aceptar este desafío! Fue hosco y divertido.

Jacob
fuente
¡Gracias! De hecho, sería genial tener una entrada con diferentes idiomas de origen y destino.
Zgarb
2

Jalea7 , 9 bytes

“ṚƓ^ṾṂ’³3

Pruébalo en línea!

Q es una función 7 (es decir, que no se ve más allá del elemento de la pila superior, y hace E / S a través de la pila), y se proporciona como un argumento de línea de comandos.

Explicación

El programa 7

El constructor universal de quine en 7 que uso aquí es:

717162234430…3

Lo primero a tener en cuenta es que el 7 principal es el equivalente del espacio en blanco inicial y no tiene ningún efecto en el programa. La única razón por la que existe es para obedecer las reglas de PPCG contra las quines literales (está codificado por el segundo 1en el programa en lugar de por sí mismo).

El resto del programa es un elemento de pila único (tiene 7s y 6s equilibrados ), que hace lo siguiente cuando se ejecuta:

717162234430…3
 1716           Push a stack element "7" onto the stack
     2          Copy it
      23        Pop and output one of the copies (selecting format 7)
        4430    Prepend it to the top of stack
             3  Output it

En otras palabras, este elemento de la pila es un programa que imprime la parte superior de la pila, con el 7antepuesto, en formato de salida 7 (que significa "imprimir literalmente, usando la misma codificación que el código fuente", y por lo tanto es claramente la mejor codificación para quines). Es bastante afortunado aquí que podamos reutilizar el literal 7para dos propósitos (el formato de salida y el espacio en blanco inicial). Claramente, al insertar algo justo antes del final 3, podemos generar una función de 7+ la entrada, en lugar de simplemente generar7 y Entrada directa.

¿Cómo llega este elemento de pila a su propio código fuente? Bueno, cuando se alcanza el final del programa, 7 evals es el elemento de la pila superior de forma predeterminada. Sin embargo, en realidad no se extrae de la pila en el proceso, por lo que el elemento literal de pila que se evalllevó todavía está allí. (En otras palabras, el programa no está leyendo su propia fuente, como lo demuestra el hecho de que no puede ver 7al inicio del programa, que es un separador de elementos de pila en lugar de ser parte de un literal, sino que consiste principalmente en un literal que obtieneeval dirige por defecto).

El programa de gelatina

Este es quizás uno de los programas Jelly menos similares a Jelly que he escrito; se compone de tres nilads ( “ṚƓ^ṾṂ’, ³, 3), que son sólo de salida en secuencia porque no se realizan operaciones en ellos. El 3es bastante obvio, solo es una constante entera. También ³es simple si conoce a Jelly: es la notación explícita de Jelly para el primer argumento de línea de comandos (que es donde Jelly normalmente toma su entrada). El resto del programa Jelly representa la mayor parte de mi constructor de quine universal 7: explotando el hecho de que todos los comandos en 7 pueden representarse usando dígitos ASCII, podemos interpretar717162234430no como una serie de comandos, o incluso como un número octal (como lo es conceptualmente), sino como un número decimal, lo que significa que no necesitamos ningún formato especial para la salida. Ese número decimal se convierte “ṚƓ^ṾṂ’en la notación entera comprimida de Jelly.

Ejemplo

Si damos 24053como programa Q, obtendremos el siguiente resultado:

717162234430240533

Pruébalo en línea!

2405 concatena el elemento superior de la pila consigo mismo:

2405   Stack   Explanation
       x
2      x|x     Duplicate top of stack
 4     x||x    Swap two stack elements, with an empty element between
  0    x|(X)   Escape the top stack element, then concatenate the top two
   5   xx      Execute the top stack element

(El último paso puede parecer un poco confuso; lo que sucede es que escapar de un elemento de la pila convierte cada comando en él de "ejecutar este comando" a "agregar este comando a la parte superior de la pila", por lo que cada comando se agrega al original elemento de pila superior a medida que se ejecuta).

Como tal, ejecutar el programa resultante R nos da dos copias de R:

7171622344302405371716223443024053

fuente
2

CJam → CJam, 13 bytes

{`"_~"+7}_~qt

Pruébalo en línea!

La entrada Qdebe ser un fragmento de código que modifique la única cadena en la pila. Qse lee de stdin.

Ejemplo

Entrada:

S*W%

Agrega un espacio entre cada dos caracteres e invierte la cadena.

Salida:

{`"_~"+S*W%}_~

Salida de la quine generalizada:

~ _ } % W * S + " ~ _ " ` {

Explicación

{`"_~"+7}_~      e# Evaluate a generalized quine in CJam that only appends a 7.
q                e# Read the input.
t                e# Replace the 7th character (0-based) with the input.

Primero evalúa el quine, para que podamos obtener su representación de cadena sin comillas dobles innecesarias. Luego reemplace la carga útil con la entrada.

Podría ser {`"_~"+ }_~7qtdonde el espacio es el marcador de posición de la carga útil. Pero cambiar la carga útil a 7ahorra un byte.

jimmy23013
fuente
1

CarbónPerl (5), 29 33 bytes

A$_=q(αA);evalβαS"\α$_β\n";printβ

Pruébalo en línea!

El programa Q de Perl debería devolver un fragmento que toma la entrada como una cadena a su lado derecho y proporciona salida en la variable $_. (Las funciones Perl arbitrarias se pueden convertir a este formulario envolviéndolas como sub x {…}; $_=x. Sin embargo, en la mayoría de los casos, la sintaxis de Perl significa que no se requiere envoltura).

Explicación

El perl

Así es como se ve el constructor universal de Perl quine:

$_=q(…"\$_=q($_);eval";print);eval

(En la mayoría de los casos, desearía reducir esto $_=q(say…"\$_=q($_);eval");eval, pero no estoy seguro de que pueda incluir código arbitrario de Perl allí).

En otras palabras, tenemos un contenedor externo $_=q(…);evalque asigna una cadena $_y luego la evalúa. Dentro de la envoltura hay "\$_=q($_);eval", es decir, una reconstrucción de la envoltura junto con su contenido mediante el uso del valor que almacenamos $_, más el código Q especificado por el usuario, más printpara imprimir la salida. (Desafortunadamente no podemos usar say; agrega una nueva línea, y eso es relevante en quines).

El carboncillo

El "punto" de esta respuesta fue producir quines generalizados en Perl, por lo que una vez que tuve una estrategia de golf para hacerlo (una que he usado en muchas otras respuestas), llegó el momento de escribir el programa P, que básicamente solo sustituye una cadena en una plantilla. Lo que quería aquí era un lenguaje que fuera bueno para imprimir cadenas constantes (idealmente comprimiéndolas un poco) e interpolar la entrada del usuario en ellas.

Después de probar algunos, me decidí por el carbón, que nunca había usado antes (y que realmente podría hacer con algo de documentación); Está diseñado para el arte ASCII, pero también es capaz de escribir cadenas en una dimensión. Los caracteres ASCII se imprimen literalmente en Carbón, lo que significa que la impresión de cadenas constantes no requiere repeticiones, y podemos usar el comando para interpolar una cadena tomada de la entrada del usuario en el programa.

Sin embargo, es posible ir (ligeramente) más corto. El constructor de quine universal Perl contiene dos secciones repetidas bastante largas. Por lo tanto, podemos usar el comando para asignarlos a variables (por ejemplo, A…αasigna a la variable α), y simplemente interpolar las variables en la cadena que estamos imprimiendo utilizando sus nombres. Eso ahorra unos pocos bytes con solo escribir la cadena literalmente.

Desafortunadamente, Charcoal también agrega una nueva línea al programa, pero eso no es un gran problema; simplemente cuesta dos bytes para \nque agregar esa nueva línea a la entrada de Q también.

Ejemplo

Si damos la entrada $_=reverse(que invierte una cadena), obtenemos la siguiente salida:

$_=q($_=reverse"\$_=q($_);eval\n";print);eval

Pruébalo en línea!

que es un quine-like que imprime su fuente al revés, como se esperaba.


fuente
1

JaleaSubcarga , 15 bytes

“(a(:^)*“S):^”j

Pruébalo en línea!

Toma la entrada de la función de subcarga Q como un argumento similar a un comando. Q debe tomar la entrada de la pila y enviar la salida a la pila, sin intentar inspeccionar los elementos más profundos de la pila (ya que no existirán).

Explicación

La subcarga

El constructor de quine universal de baja carga utilizado aquí es:

(a(:^)*…S):^

La mayor parte del programa es un solo literal. Seguimos eso :^, que lo copia, luego evalúa una copia (dejando la otra copia en la pila).

Cuando el literal comienza a evaluar, ejecutamos a(escape, que lo devuelve a la misma forma que el programa original A) y (:^)*(que se adjunta :^), reconstruyendo así el código fuente de todo el programa. Luego podemos ejecutar la función Q para transformar esto de manera arbitraria e imprimir el resultado con S.

La gelatina

Esta vez no puedo usar el carbón porque un intérprete de validación de Underload se bloquea al final del programa si el programa termina con una nueva línea. (Algunos intérpretes de baja carga, como el que está en TIO, no aplican esta regla, pero quería ser adecuadamente portátil). Desafortunadamente, Charcoal agrega naturalmente nuevas líneas finales a su salida. En cambio, usé Jelly, que es casi tan conciso en casos simples como este; el programa consiste en una lista literal con dos elementos ( ““”), y los une en la entrada ( j), interpolando así la entrada del usuario en el programa.

Ejemplo

Usando la entrada :S^(imprima una copia, luego evalúe el original), obtenemos el siguiente programa Underload:

(a(:^)*:S^S):^

Pruébalo en línea!

Esto se imprime infinitamente muchas veces, de una manera bastante interesante: después de realizar el comportamiento normal de quine, se ejecuta evalen una copia de lo que genera. Eso hace que todo el programa reconstruido vuelva a ejecutarse, indefinidamente (Underload es recursivo). Encargarse de uno mismo y hacer un en evalrealidad es la única forma de hacer un bucle infinito en Underload.


fuente
El carbón de leña ya no agrega nuevas líneas finales (yay)
solo ASCII
1

RProgN 2 , 11 bytes

'{`{.%s}{'F

Explicación del programa

'{`{.%s}{'F
'{`{.%s}{'  # Push the string "{`{.%s}{" to the stack.
          F # Format the input with the top of the stack as a template. Which produces {`{.<INPUT>}{

Quine Explination

La quine que se produce es simple, pero utiliza la funcionalidad de controladores de funciones inigualables en RProgN2 para crear una quine corta y dulce, llamada quine "Looping". Es un concepto sorprendentemente similar a una <> <quine.

{`{.}{
{`{.}   # Push the function {`{.} to the stack.
     {  # Try to define a new function, fail, loop back to index 1. (Which in turn, skips the function definition.)
 `{     # Push the string "{" to the stack.
   .    # Concatenate the top two values of the stack, which stringifies the function, then appends { to it.
    }   # Try to terminate a function, fail quietly, and terminate the program.

Por supuesto, debido a la estructura de este quine, todo lo que no sea un verdadero no-ops (que no se encadena) puede colocarse después de la función concatenate, y

Algunas quines

  • {`{.i}{: Salidas {}i.{`{. ies solo la función "inversa", por lo que este programa se genera invertido.
  • {`{.S§.}{: Salidas ..S`{{{}§. Sconvierte la cadena en una pila de caracteres, §clasifica la pila lexográficamente, luego la .une de nuevo y se ordena.

Pruébalo en línea!

Un taco
fuente