Programa que se permuta para codificar una cadena (variante quine)

16

Escriba un programa que imprima la siguiente línea de 80 caracteres:

Este programa de codegolf.stackexchange.com se permuta para codificar una cadena.

luego acepta una línea de entrada, luego imprime su código fuente con sus puntos de código posiblemente reordenados (ninguno agregado y ninguno eliminado). Cuando se ejecuta ese código, tiene que suceder lo mismo, excepto que la línea impresa sería la línea de entrada más reciente.

La expresión regular de estilo Perl ^[A-Za-z0-9. ]{80}$coincidirá con cualquier línea de entrada. No puede hacer ninguna suposición adicional.

El puntaje de una presentación es el número de puntos de código en su código fuente menos 94 . Más bajo es mejor.

El código no debe hacer nada que sea inaceptable en un quine ( por ejemplo , lectura de archivos). En particular, cualquier presentación con una puntuación negativa debe ser trampa de alguna manera, ¡como 93! es menor que 64 80 .

Agregado 2014-04-21: El código fuente completo de su programa debe estar bien formado en la codificación de caracteres bajo la cual cuenta los puntos de código. Por ejemplo, no puede usar 80 bytes consecutivos en el rango de bytes finales UTF-8 (80..BF) y contar cada uno como un solo CARACTER DE REEMPLAZO U + FFFD (o peor, como no es un punto de código).

Además, si la codificación permite múltiples formas de codificar un punto de código ( por ejemplo, SCSU ), su programa, así como todos los programas que genera directa o indirectamente, solo deben usar uno de ellos (o al menos todos deben ser tratados de manera equivalente en todo el código )

Por favor levantese
fuente
Después de volver a leer su pregunta, no estoy seguro de si mi respuesta hace exactamente lo que tenía en mente. ¿Está bien conectar la nueva cadena al programa o tiene que iniciar un mensaje interactivo?
Dennis
@ Dennis: Esa no es la razón por la cual su respuesta no es aceptable. Más bien, lee la entrada antes de imprimir "Este programa desde [...]".
favor
A eso me refería, simplemente no lo expresé bien. El intérprete de GolfScript lee todo lo que se canaliza antes de comenzar a ejecutar el script. La única forma de evitar esto es iniciar un aviso, lo que hace imposible la tubería.
Dennis
Hola, estoy intentando esto en JavaScript. ¿Parece imposible hacer una quine sin leer el texto entre las etiquetas <script>? ¿Cuál es el propósito de permutar el código fuente? Dices "posiblemente reordenado"; ¿Esto significa permutar solo si es necesario?
bacchusbeale

Respuestas:

5

GolfScript, 231 162 131

'1àâ4ÿaVo5GùpZBtiXOürsóNîMmWåKHc09JdñúêyzíECäYïhDU ãáIFõ6é8òRìjTv23ønuðLwxfSkôbëAelqý.çèPQ
öûg7'{0@.$[{}/]:&\{@2$,*2$2$?+@@^}/{;65base}:b~{&=}%''+puts'"#{`head -1`}"'~{&?}%)b[94,{)1$1$%@@/}/;]-1%&\[{1$=.@^}/]"'".@+\+\'.~'}.~

Cómo funciona

Comenzamos eligiendo 94 caracteres diferentes que se permutarán para codificar una cadena. Cualquier 94 caracteres funcionaría, pero elegimos lo siguiente para jugar al golf:

\n .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
àáâãäåçèéêëìíîïðñòóôõöøùúûüýÿ

Llamemos al conjunto de estos caracteres "&".

La línea de entrada siempre contendrá 81 caracteres (incluido el LF). Todos esos caracteres están presentes en los primeros 65 caracteres de "&". Esta es la única razón para elegir caracteres en los 128 bytes superiores.

Reemplazamos cada carácter de la cadena por su índice en "&", por lo que LF se convierte en 0, el espacio se convierte en 1, etc.

Consideramos que los 81 números obtenidos son los dígitos de un solo número base 65. Llamemos a este número "N".

Ahora, enumeramos todas las permutaciones posibles de "&" y recuperamos la permutación correspondiente al número de arriba. Esto se logra de la siguiente manera:

  1. Establecer c = 1y A = [].
  2. Anteponer N % ca A.
  3. Establecer N = N / cy c = c + 1.
  4. Si c < 95, vuelve a 2.
  5. Establecer i = 0y s = "".
  6. Recupere el carácter &[A[i]], agréguelo a "s" y quítelo de "&".
  7. Conjunto i = i + 1.
  8. Si i < 94vuelve a 6.

Supongamos que tenemos bloques de código "E" y "D" que codifican y decodifican una cadena como se explicó anteriormente.

Ahora, necesitamos un contenedor para esos bloques de código que cumpla con los requisitos de la pregunta:

'encoded string'{\.$[{}/]:&; D puts '"#{`head -1`}"'~ E "'".@+\+\'.~'}.~

Esto hace lo siguiente:

  • {…}.~define un bloque, lo duplica y ejecuta la segunda copia. La primera copia permanecerá en la pila.

  • \.$ intercambia la cadena codificada con el bloque y crea una copia de la cadena codificada, con caracteres ordenados.

  • [{}/]:&; convierte la cadena de arriba en una matriz, la guarda en "&" y la descarta.

  • D puts decodifica la cadena codificada e imprime el resultado.

  • '"#{`head -1`}"'~lee una línea de entrada ejecutando head -1en el shell.

  • E "'".@+\+ codifica la cadena y antepone y agrega una comilla simple.

  • \'.~'intercambia la cadena codificada y el bloque y agrega la cadena '.~'.

  • Una vez ejecutado el bloque, GolfScript imprime el contenido de la pila (cadena codificada, bloque '.~') y sale.

"E" se puede definir de la siguiente manera:

{&?}%        # Replace each character by its index in “&”.
);           # Remove the last integer from the array, since it corresponds to the LF.
65base       # Convert the array to an integer “N” by considering it a base 65 number.
[            #
  94,        # For each integer “c” in 0 … 93:
  {          #
    )        # Increment “c”.
    1$1$%    # Push “N % c”.
    @@/      # Rotate “N % c” below “N” and “c” and divide the first by the latter.
  }/;        # Discard “N”.
]            # Collect the results of “N % c” in an array “A”.
-1%          # Reverse “A”.
&\           # Push “&” and swap it with “A”.
[            #
  {          # For each “j” in “A”:
    1$=.[]+  # Push “&[j] [&[j]]”.
    @^       # Rotate “&” on top of “[&[j]]” and take their symmetric difference.
  }/         #
]            # Collect the charcters into an array.

"D" se puede definir de la siguiente manera:

0&           # Push 0 (initial value of the accumulator “A”) and “&”.
@            # Rotate the encoded string on top of “&”.
{            # For each character “c” of the encoded string:
    @2$,*    # Rotate “A” on top of the stack and multiply it by the length of “&”.
    2$2$?+   # Get the index of “c” in “&” and add it to “A”.
    @@^      # Rotate “A” below “&” and “c” and take their symmetric difference.
}/;          # Discard “&”.
65base       # Convert “A” into the array of its digits in base 65.
{&=}%        # Replace each digit by the corresponding character in “&”.
''+          # Convert the resulting array into a string.

Golf final:

  • Reemplace \.$[{}/]:&;0&@con 0@.$[{}/]:&\para guardar dos caracteres.

  • Defina la función {;65base}:bpara guardar un personaje.

  • Elimine todos los espacios en blanco excepto el LF final y el LF en la cadena.

Ejemplo

$ # Create GolfScript file using base64 to avoid encoding issues.
$ base64 > permute.gs -d <<< JzHg4jT/YVZvNUf5cFpCdGlYT/xyc/NO7k1tV+VLSGMwOUpk8frqeXrtRUPkWe9oRFUg4+FJRvU26TjyUuxqVHYyM/hudfBMd3hmU2v0YutBZWxx/S7n6FBRCvb7ZzcnezBALiRbe30vXTomXHtAMiQsKjIkMiQ/K0BAXn0vezs2NWJhc2V9OmJ+eyY9fSUnJytwdXRzJyIje2BoZWFkIC0xYH0iJ357Jj99JSliWzk0LHspMSQxJCVAQC99LztdLTElJlxbezEkPS5AXn0vXSInIi5AK1wrXCcufid9Ln4K
$
$ # Set locale to en_US (or any other where one character is one byte).
$ LANG=en_US
$
$ # Go back and forth between two different strings.
$ # Second and sixth line are user input, not output from the script.
$
$ golfscript permute.gs | tee >(tail -n+2 > tmp.gs) && golfscript tmp.gs && rm tmp.gs
This program from codegolf.stackexchange.com permutes itself to encode a string.
Permuting source code code points to encode a string is a certain quine variant.
'18äJoS3sgV9qdçëxm0ÿKMNe5íPî.Htn2ciâIuøbRZéð4AwB7áìUüöôWõèûfñåLàóDrhQlO6
pTaýzòkùYCyFêïãG júEvX'{0@.$[{}/]:&\{@2$,*2$2$?+@@^}/{;65base}:b~{&=}%''+puts'"#{`head -1`}"'~{&?}%)b[94,{)1$1$%@@/}/;]-1%&\[{1$=.@^}/]"'".@+\+\'.~'}.~
Permuting source code code points to encode a string is a certain quine variant.
This program from codegolf.stackexchange.com permutes itself to encode a string.
'1àâ4ÿaVo5GùpZBtiXOürsóNîMmWåKHc09JdñúêyzíECäYïhDU ãáIFõ6é8òRìjTv23ønuðLwxfSkôbëAelqý.çèPQ
öûg7'{0@.$[{}/]:&\{@2$,*2$2$?+@@^}/{;65base}:b~{&=}%''+puts'"#{`head -1`}"'~{&?}%)b[94,{)1$1$%@@/}/;]-1%&\[{1$=.@^}/]"'".@+\+\'.~'}.~
$
$ # Sort all characters from the original source code and hash them.
$ fold -1 permute.gs | sort | md5sum
b5d978c81df5354fcda8662cf89a9784  -
$
$ # Sort all characters from the second output (modified source code) and hash them.
$ golfscript permute.gs | tail -n+2 | fold -1 | sort | md5sum
Permuting source code code points to encode a string is a certain quine variant.
b5d978c81df5354fcda8662cf89a9784  -
$
$ # The hashes match, so the characters of the modified source code are a permutation
$ # of the character of the original one.
Dennis
fuente
224 menos 94 es 130.
mbomb007
¿Podrías dar más detalles?
Dennis
1

Perl, 1428 1099

Tiene 1193 caracteres ASCII (incluidos 960 dígitos binarios permutados). 1193 - 94 = 1099

$s='010011100001100010101100111111101001101011101000100000101011011010100110111111011111101011101000100110111111011100101000011101011110100000101000100101011111111110101100101101011010011100100100011110110001011100100001011010100111100000011110111110011100101000100110111111101001011110101011100110101110101101011110101100111111100010101101101100011110100101011111111111101101101000111111011110100111011100101000011101011110111111011010111111101100101101101011100010100111100000111110';$_=q{$i=join'',A..Z,a..z,0..9,'. ';print map({substr$i,oct'0b'.$_,1}$s=~/.{6}/g),$/;chop($s=<>);$s=join'',map{sprintf"%06b",index$i,$_}$s=~/./g;$t=join'',map{$_ x(480-(()=$s=~/$_/g))}0,1;print"\$s='$s';\$_=q{$_};eval#$t"};eval#000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

Mi primer diseño

Antes de tomar una sugerencia de Dennis para cambiar a binario, mi programa permutaba los dígitos octales.

Mi primer diseño codifica cada cadena en 160 dígitos octales, con 2 dígitos por carácter. Esta codificación tiene 100 8 = 64 caracteres diferentes. El sistema octal tiene 8 dígitos diferentes. El programa debe tener 160 copias de cada dígito, por lo que permuta 8 × 160 = 1280 dígitos.

Mantengo 160 dígitos $sy los otros 1120 dígitos $t. Comienzo con un programa que no es una quine, sino que solo imprime las asignaciones para $sy $tpara la próxima ejecución. Eso es todo:

$s = '2341425477515350405332467737535046773450353640504537765455323444366134413247403676345046775136534656553654774255543645377755507736473450353677327754555342474076';
$t = '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777';

# $i = character map of 64 characters, such that:
#  substr($i, $_, 1) is the character at index $_
#  index($i, $_) is the index of character $_
$i = join '', 'A'..'Z', 'a'..'z', '0'..'9', '. ';

# Decode $s from octal, print.
#  1. ($s =~ /../g) splits $s into a list of pairs of octal digits.
#  2. map() takes each $_ from this list.
#  3. oct() converts $_ from an octal string to a number.
#  4. substr() on $i converts number to character.
#  5. print() outputs the characters from map() and a final "\n".
print map({ substr $i, oct, 1 } $s =~ /../g), "\n";

# Read new $s, encode to octal.
#  1. ($s = <>) reads a line.
#  2. chop($s) removes the last character of $s, the "\n".
#  3. ($s =~ /./g) splits $s into characters.
#  4. map() encodes each character $_ as a pair of octal digits.
#  5. join() concatenates the pairs from map().
chop($s = <>);
$s = join '', map { sprintf "%02o", index $i, $_ } $s =~ /./g;

# Make new $t.
#  1. map() takes each $_ from 0 to 7.
#  2. $_ x (160 - (() = $s =~ /$_/g)) makes a string where $_ repeats
#     160 times, minus the number of times that $_ appears in $s.
#  3. join() concatentates the strings from map().
$t = join '', map { $_ x (160 - (() = $s =~ /$_/g)) } 0..7;

# Print the new assignments for $s and $t.  This is not yet a quine,
# because it does not print the rest of the program.
print "\$s = '$s';\n\$t = '$t';\n";

(() = $s =~ /$_/g))es una asignación a una lista vacía de variables. Tomo este truco del tutorial de contexto en PerlMonks . Fuerza el contexto de la lista en el operador de coincidencia =~. En contexto escalar, la coincidencia sería verdadera o falsa, y necesitaría un ciclo como $i++ while ($s =~ /$_/g)para contar las coincidencias. En el contexto de la lista, $s =~ /$_/ges una lista de coincidencias. Puse esta lista en el contexto escalar de una resta, por lo que Perl cuenta los elementos de la lista.

Para hacer una quine, tomo el formulario $_=q{print"\$_=q{$_};eval"};evalde Perl quines en Rosetta Code . Éste asigna una cadena q{...}a $_y luego llama eval, para que pueda tener mi código en una cadena y también ejecutarlo. Mi programa se convierte en una quine cuando envuelvo mi tercera y última línea en $_=q{y };eval, y cambio mi última printa print "\$s = '$s';\n\$t = '$t';\n\$_=q{$_};eval".

Finalmente, juego mi programa de golf cambiando la primera asignación $ta un comentario y eliminando caracteres adicionales.

Tiene 1522 caracteres ASCII (incluidos 1280 dígitos octales permutados).
1522 - 94 = 1428

$s='2341425477515350405332467737535046773450353640504537765455323444366134413247403676345046775136534656553654774255543645377755507736473450353677327754555342474076';#0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777
$_=q{$i=join'','A'..'Z','a'..'z','0'..'9','. ';print map({substr$i,oct,1}$s=~/../g),"\n";chop($s=<>);$s=join'',map{sprintf"%02o",index$i,$_}$s=~/./g;$t=join'',map{$_ x(160-(()=$s=~/$_/g))}0..7;print"\$s='$s';#$t\n\$_=q{$_};eval"};eval

El cambio a binario

En los comentarios, Dennis notó que 960 dígitos binarios permutados serían menos de 1280 dígitos octales. Entonces graficé el número de dígitos permutados para cada base de 2 a 16.

Maxima 5.29.1 http://maxima.sourceforge.net
using Lisp ECL 13.5.1
...
(%i36) n : floor(x);
(%o36)                             floor(x)
...
(%i41) plot2d(n * ceiling(log(64) / log(n)) * 80, [x, 2, 16],
              [xlabel, "base"], [ylabel, "number of permuted digits"]);
(%o41) 

gráfico con base en el eje x, número de dígitos permutados en el eje y

Aunque la base 8 es un mínimo local, las bases 2 y 3 y 4 empatan para la mejor base, con 960 dígitos permutados. Para el código de golf, la base 2 es mejor porque Perl tiene conversiones para la base 2.

Reemplazar 1280 dígitos octales con 960 dígitos binarios ahorra 320 caracteres.

Cambiar el código de octal a binario cuesta 8 caracteres:

  • Cambio octa oct'0b'.$_costos 7.
  • Cambiar /../ga /.{6}/gcostos 2.
  • Cambiar "%02o"a "% 06b" `cuesta 0.
  • Cambiar 160a 480costos 0.
  • Cambiar 0..7a 0,1guardados 1.

Aprendí algunos consejos de golf de Perl . Guardan 14 caracteres:

  • Cambiar 'A'..'Z','a'..'z','0'..'9'a A..Z,a..z,0..9, usando palabras y números desnudos, guarda 12 caracteres.
  • Cambiar "\n"a $/guardar 2 caracteres.

Guardo 3 caracteres moviendo el #$tcomentario al final del archivo. Esto elimina la nueva línea que finaliza el comentario y un literal \nen la línea.

Estos cambios ahorran un total de 329 caracteres y reducen mi puntaje de 1428 a 1099.

kernigh
fuente
1
El uso de dígitos binarios en lugar de octales requeriría "solo" 960 caracteres permutables.
Dennis
@ Dennis Gracias por el consejo! Hice el cambio a binario (guardando 312 caracteres). Mientras estuve aquí, jugué 17 personajes más.
kernigh