Perl, 1428 1099
Celui-ci contient 1193 caractères ASCII (dont 960 chiffres binaires permutés). 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
Mon premier design
Avant de prendre une suggestion de Dennis pour passer au binaire, mon programme permutait les chiffres octaux.
Mon premier design encode chaque chaîne en 160 chiffres octaux, avec 2 chiffres par caractère. Cet encodage a 100 8 = 64 caractères différents. Le système octal a 8 chiffres différents. Le programme doit avoir 160 copies de chaque chiffre, donc il permute 8 × 160 = 1280 chiffres.
Je garde 160 chiffres $s
et les 1120 autres chiffres $t
. Je commence avec un programme qui n'est pas une quine, mais imprime uniquement les affectations vers $s
et $t
pour la prochaine exécution. Ça y est:
$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))
est une affectation à une liste vide de variables. Je prends cette astuce dans le didacticiel de contexte de PerlMonks . Il force le contexte de liste sur l'opérateur de correspondance =~
. Dans un contexte scalaire, la correspondance serait vraie ou fausse, et j'aurais besoin d'une boucle comme $i++ while ($s =~ /$_/g)
pour compter les correspondances. Dans un contexte de liste, $s =~ /$_/g
est une liste de correspondances. J'ai placé cette liste dans le contexte scalaire d'une soustraction, donc Perl compte les éléments de la liste.
Pour faire une quine, je reprends le formulaire $_=q{print"\$_=q{$_};eval"};eval
des quines Perl de Rosetta Code . Celui-ci affecte une chaîne q{...}
à $_
puis appelle eval
, afin que je puisse avoir mon code dans une chaîne et l'exécuter également. Mon programme devient une quine lorsque j'encapsule ma troisième à la dernière ligne dans $_=q{
et };eval
, et change ma dernière print
en print "\$s = '$s';\n\$t = '$t';\n\$_=q{$_};eval"
.
Enfin, je joue à mon programme en remplaçant la première affectation $t
par un commentaire et en supprimant les caractères supplémentaires.
Cela a 1522 caractères ASCII (dont 1280 chiffres octaux permutés).
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
Le passage au binaire
Dans les commentaires, Dennis a remarqué que 960 chiffres binaires permutés seraient inférieurs à 1280 chiffres octaux. J'ai donc représenté graphiquement le nombre de chiffres permutés pour chaque base de 2 à 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)
Bien que la base 8 soit un minimum local, les bases 2 et 3 et 4 sont égales pour la meilleure base, à 960 chiffres permutés. Pour le golf de code, la base 2 est la meilleure car Perl a des conversions pour la base 2.
Le remplacement de 1280 chiffres octaux par 960 chiffres binaires enregistre 320 caractères.
Passer d'un code octal à un code binaire coûte 8 caractères:
- Modification
oct
des oct'0b'.$_
coûts 7.
- Modification
/../g
des /.{6}/g
coûts 2.
- Changer
"%02o"
pour "% 06b" `coûte 0.
- Modification
160
des 480
coûts 0.
- Passer
0..7
en 0,1
sauvegardes 1.
J'ai appris quelques conseils de golf Perl . Ils sauvent 14 caractères:
- Changer
'A'..'Z','a'..'z','0'..'9'
en A..Z,a..z,0..9
utilisant des mots nus et des nombres nus, enregistre 12 caractères.
- Changer
"\n"
pour $/
enregistrer 2 caractères.
J'enregistre 3 caractères en déplaçant le #$t
commentaire à la fin du fichier. Cela supprime la nouvelle ligne qui termine le commentaire et un littéral \n
dans la quine.
Ces modifications permettent d'économiser un total de 329 caractères et de réduire mon score de 1428 à 1099.