Perl, 1428 1099
Possui 1193 caracteres ASCII (incluindo 960 dígitos binários 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
Meu primeiro design
Antes de receber uma sugestão de Dennis para mudar para binário, meu programa permutava dígitos octais.
Meu primeiro design codifica cada string em 160 dígitos octais, com 2 dígitos por caractere. Essa codificação possui 100 8 = 64 caracteres diferentes. O sistema octal possui 8 dígitos diferentes. O programa deve ter 160 cópias de cada dígito, para permitir 8 × 160 = 1280 dígitos.
Eu mantenho 160 dígitos $s
e os outros 1120 dígitos $t
. Começo com um programa que não é adequado, mas apenas imprime as atribuições para $s
e $t
para a próxima execução. É isso:
$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))
é uma atribuição a uma lista vazia de variáveis. Eu tomo esse truque no tutorial de contexto do PerlMonks . Força o contexto da lista no operador de correspondência =~
. No contexto escalar, a correspondência seria verdadeira ou falsa, e eu precisaria de um loop $i++ while ($s =~ /$_/g)
para contar as correspondências. No contexto da lista, $s =~ /$_/g
há uma lista de correspondências. Eu coloquei essa lista no contexto escalar de uma subtração, para que o Perl conte os elementos da lista.
Para fazer um quine, tomo o formulário $_=q{print"\$_=q{$_};eval"};eval
dos perl quines do Rosetta Code . Este atribui uma string q{...}
para $_
e depois chama eval
, para que eu possa ter meu código em uma string e também executá-lo. Meu programa se torna um Quine quando eu envolver minha terceira a última linha em $_=q{
e };eval
, e mudar o meu passado print
para print "\$s = '$s';\n\$t = '$t';\n\$_=q{$_};eval"
.
Finalmente, eu jogo meu programa alterando a primeira tarefa para $t
para um comentário e removendo caracteres extras.
Possui 1522 caracteres ASCII (incluindo 1280 dígitos octais 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
A mudança para binário
Nos comentários, Dennis notou que 960 dígitos binários permutados seriam menores que 1280 dígitos octais. Então, eu gráfico o 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)
Embora a base 8 seja o mínimo local, as bases 2 e 3 e 4 são a melhor base, com 960 dígitos permutados. Para o código de golfe, a base 2 é melhor porque o Perl tem conversões para a base 2.
A substituição de 1280 dígitos octais por 960 dígitos binários salva 320 caracteres.
Mudar o código de octal para binário custa 8 caracteres:
- Alterar
oct
para oct'0b'.$_
custos 7.
- Alterar
/../g
para /.{6}/g
custos 2.
- Altere
"%02o"
para "% 06b" `custa 0.
- Altere
160
para 480
custos 0.
- Alterar
0..7
para 0,1
salvar 1.
Aprendi algumas dicas de golfe em Perl . Eles economizam 14 caracteres:
- Altere
'A'..'Z','a'..'z','0'..'9'
para A..Z,a..z,0..9
, usando palavras de barra e números simples, salva 12 caracteres.
- Mude
"\n"
para $/
salva 2 caracteres.
Eu salvo 3 caracteres movendo o #$t
comentário para o final do arquivo. Isso remove a nova linha que termina o comentário e um literal \n
no quine.
Essas alterações salvam um total de 329 caracteres e reduzem minha pontuação de 1428 para 1099.