Consejos para escribir quines


30

Un es un programa que produce resultados idénticos al código fuente del programa. En este sitio web, generalmente solo nos preocupamos por las quines adecuadas (en el momento de la redacción, la definición actual es "alguna parte de la salida está codificada por una parte diferente del programa").

¿Qué consejo tiene para escribir quines adecuados o programas con propiedades similares a quines? Como de costumbre, cada consejo debe tener una respuesta diferente.

tips  quine 

Respuestas:


14

Úselo evalpara reducir la necesidad de copiar código

La mayoría de las cuotas requieren dos copias del código; uno para ser ejecutado, uno como datos. Esto puede terminar duplicando la longitud del código fuente, haciendo que sea más difícil de mantener y empeorando el puntaje si está escribiendo el quine para una competencia de .

Fusionar las dos copias significa que una pieza de información debe usarse para dos propósitos. Intentar tratar su código como datos a menudo no es posible, y generalmente se considera trampa cuando lo es. Sin embargo, el tratamiento de los datos como código se puede hacer en muchos idiomas mediante el uso de una función incorporada, generalmente llamada eval. Como tal, su quine básicamente consiste en almacenar el cuerpo principal de su quine en una variable (para que pueda referirse a él más de una vez), luego evaluar esa variable.

Aquí hay un ejemplo de cómo funciona esto (el ejemplo está escrito en Python, pero algo similar funciona en muchos otros idiomas):

d='print("d="+repr(d)+"\\neval(d)")'
eval(d)

2
@QPaysTaxes: mi objetivo es hacer que mi código aquí sea tan legible y fácil de mantener como lo permita la condición de victoria. Por desgracia, eso es todavía típicamente indistinguible de ruido de línea ASCII (o ruido de línea regular si simplemente estoy usando la jalea) de las personas que no están acostumbrados al lenguaje.

14

Aproveche el formato de cadena

Una de las formas más fáciles de crear una quine es definir una cadena, luego colocar la cadena dentro de sí misma con el formato de cadena.

s='s=%r;print s%%s';print s%s

Entonces, en este ejemplo de Python quine, declaramos una cadena con la primera parte igual a lo que esté antes de la cadena s=, luego permitimos que la cadena se inserte con formato %r, y finalmente colocamos lo que viene después de la cadena para imprimirla y formatearla . La nueva línea final se debe a que printimprime una nueva línea final.

Entonces, la plantilla es realmente esto, en Python:

<A>'<A>%r<B>'<B>

Para expandir la quine existente con más código:

<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>

9

Funciones Stringified

En varios idiomas, los objetos de función (o construcciones equivalentes) almacenan implícitamente su código fuente y lo devolverán cuando se convierta en una cadena. Esto permite quines compactas sin usar la evaluación de cadena . Un ejemplo notable de tal lenguaje es JavaScript:

function f(){console.log(f+"f()")}f()

Este código define y llama a una función fque, cuando se llama, imprime su propio código fuente seguido de una llamada a sí mismo. La única parte del programa que debe duplicarse es la llamada a la función f(). Por supuesto, el cuerpo de la función puede incluir una "carga útil" arbitraria de código que también se ejecutará cuando se llame a la función.


Una versión más compacta del mismo truco funciona en los lenguajes de golf GolfScript :

{".~"}.~

y CJam :

{"_~"}_~

Cada uno de estos quines primero define un bloque de código anónimo (encerrado entre llaves), que se comporta de manera muy similar a un objeto de función en JavaScript: puede ejecutarse y, si está en cadena, devuelve su código fuente. El resto del código ( .~en GolfScript o _~en CJam) ejecuta el bloque y deja una copia en la pila. El código dentro del bloque luego empuja una cadena en la pila que repite el código fuera del bloque. Cuando el intérprete sale, automáticamente encadena e imprime todo lo que queda en la pila. Al igual que con el ejemplo de JavaScript, estos bloques de código también se pueden hacer para transportar y ejecutar una carga útil arbitraria de código adicional sin tener que duplicarlo.


9

Use delimitadores de cadena que anidan sin escapar

A menudo, una de las partes más difíciles de escribir un quine es el paso de escape. Esto es necesario en casi todas las líneas; El problema es que está almacenando datos de alguna manera, y necesita replicar el código que almacena los datos en la salida de la quine. Ese código contendrá una forma escapada de los datos, por lo que el programa verá una forma no escapada y deberá volver a escapar.

La forma más fácil de manejar el paso de escape es si las formas de datos escapadas y no escapadas difieren solo en la presencia o ausencia de delimitadores de cadena. Escapar es, por lo tanto, una simple cuestión de agregar un nuevo par de delimitadores de cadena alrededor de la cadena. Desafortunadamente, esto solo puede funcionar si los delimitadores de cadena pueden expresarse en los datos sin escapar.

Perl es un buen ejemplo de un lenguaje donde funciona este truco. Aunque sus delimitadores de cadena habituales son "…"o '…', los q(…)nidos menos utilizados , permiten escribir este tipo de quine:

$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print

Este es un código + datos quine. s///es una operación de reemplazo de cadena regex; usamos 0como marcador, y lo combinamos dentro de la expresión regular como \d("cualquier dígito"), para evitar usar el marcador más de una vez (aunque como otra optimización, podríamos haberlo usado 0nuevamente, porque Perl s///solo reemplaza la primera aparición por defecto). Tenga en cuenta que no se necesita un paso de escape explícito aquí, ya que los q(…)delimitadores se pueden incluir literalmente en la cadena de datos.


8

Código + datos quines

La estructura más general para una quine se parece a este pseudocódigo:

data = " una versión escapada de todo el programa,
        con esta cadena reemplazada por un marcador "
programa = data.replace (
  una expresión que evalúa el marcador pero no lo menciona ,
  escapado (datos))
programa de impresión;

Esta estructura se puede usar para escribir una quine (bastante ingenua) en la mayoría de los idiomas. Sin embargo, tiende a obtener puntajes bastante malos en la mayoría de los sistemas de puntaje, ya que tiene que escribir la totalidad del programa dos veces. Sin embargo, la mayoría de las estructuras de quine pueden considerarse optimizaciones de esta.

Hay algunas sutilezas en esto. En algunos idiomas, la parte más difícil de realizar esta operación es escribir el código de escape; en muchos idiomas, es difícil producir el marcador sin mencionar su nombre; y en algunos lenguajes esotéricos, tendrás que inventar tu propio tipo de literal de cadena. Sin embargo, las tres operaciones tienden a no causar demasiados problemas.

Por ejemplo, podemos escribir una quine de Python escapando de una cadena usando repr, y usando la x"cadena de secuencia de 2 caracteres (que es representable como "x\"", es decir, no usando la secuencia x"en la representación de cadena de la cadena misma) como marcador:

d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))

2
Puede valer la pena señalar (posiblemente en otra respuesta) que insertar la cadena en la posición de un marcador a menudo es costoso en esolangs, y puede valer la pena estructurar el código de modo que la cadena en sí sea lo primero o lo último (tal vez separado de al final con uno o dos caracteres que puede codificar) para que sepa a dónde tiene que ir.
Martin Ender

@ MartinEnder: Estoy de acuerdo en que vale la pena mencionarlo, pero probablemente sea otra respuesta (en lugar de un comentario o una edición en esta respuesta). La mayoría de los consejos de quine son modificaciones a esta estructura general, por lo que quería obtenerlo primero como un consejo por sí solo, ya que muchas personas no tienen idea de por dónde empezar a escribir un quine.

Una alternativa a un marcador es usar dos cadenas, lo hice para Glass .
Ørjan Johansen el

4

Explotar código fuente de envoltura

En bastantes idiomas (principalmente idiomas 2D), el código fuente puede ajustarse; bajo ciertas circunstancias (por ejemplo, en Befunge-98, si su programa es de una sola línea) pasar el final del programa lo llevará de regreso al inicio del programa. Este tipo de comportamiento no lineal significa que puede escribir código que está dentro y fuera de un literal de cadena al mismo tiempo; un inigualable "(o lo que sea el delimitador de cadena) le dará efectivamente una cadena que contiene todo el resto del programa (excepto el "propio).

Un problema con el uso de este truco es que obtendrá la cadena como se ve desde el punto de vista del ", en lugar de desde el inicio del programa (como desearía). Como tal, es probable que sea más fácil reorganizar el programa para que "aparezca al principio o al final. Esto a menudo significa dividir su programa en varias partes y utilizar cualquier comando de control de flujo interesante / inusual que tenga su idioma (la mayoría de los idiomas que permiten que los literales de cadena envuelvan el programa tienen una buena selección de estos).

Un buen ejemplo es la quine Befunge-98 de @ Justin :

<@,+1!',k9"

Lo incomparable "al final del programa envuelve todo el programa en un literal de cadena, por lo que (corriendo de derecha a izquierda debido al <inicio) todo lo que tenemos que hacer es generar el programa ( 9k), luego generar la comilla doble ( '!1+,) y salida ( @). Esto ahorra la necesidad de dos copias del programa (una como código, otra como datos); Las dos copias son el mismo código, solo interpretadas de diferentes maneras.


4

Recuerda la estructura de una quine

Me gusta pensar en quines como tres partes, en lugar de 2:

  • Parte 1: generar una representación de datos de las partes 2 y 3.
  • Parte 2: utilice los datos para imprimir algorítmicamente la parte 1.
  • Parte 3: Decodifique la representación para imprimir las partes 2 y 3.

Esto puede hacer que sea más fácil pensar en quines. Aquí hay una línea de Python, con cada línea correspondiente a una parte:

s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)

A veces, utiliza una evalo similar para eliminar la duplicación, pero en general he descubierto que esto ayuda a escribir quines simples.

Echemos un vistazo a dos quines diferentes de subcarga. Este es el primero:

(:aSS):aSS

La primera parte es (:aSS), que genera la representación de datos. El segundo es :aS, que imprime (:aSS). La tercera parte es S, que imprime :aSS.

Aquí está la segunda quine:

(:aS(:^)S):^

Al principio, esto no parece encajar. Pero si expande la quine, obtiene:

(:aS(:^)S):aS(:^)S

Ahora (:aS(:^)S)es la parte 1, :aSes la parte 2 y (:^)Ses la parte 3.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.