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 $s
y los otros 1120 dígitos $t
. Comienzo con un programa que no es una quine, sino que solo imprime las asignaciones para $s
y $t
para 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 =~ /$_/g
es 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"};eval
de 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 print
a print "\$s = '$s';\n\$t = '$t';\n\$_=q{$_};eval"
.
Finalmente, juego mi programa de golf cambiando la primera asignación $t
a 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)

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
oct
a oct'0b'.$_
costos 7.
- Cambiar
/../g
a /.{6}/g
costos 2.
- Cambiar
"%02o"
a "% 06b" `cuesta 0.
- Cambiar
160
a 480
costos 0.
- Cambiar
0..7
a 0,1
guardados 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 #$t
comentario al final del archivo. Esto elimina la nueva línea que finaliza el comentario y un literal \n
en la línea.
Estos cambios ahorran un total de 329 caracteres y reducen mi puntaje de 1428 a 1099.