Perl 1428, 1099
Dieser enthält 1193 ASCII-Zeichen (einschließlich 960 permutierter Binärziffern). 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
Mein erster Entwurf
Bevor ich Dennis einen Vorschlag machte, auf Binär umzuschalten, permutierte mein Programm Oktalziffern.
Mein erstes Design codiert jede Zeichenfolge in 160 Oktalzeichen mit 2 Zeichen pro Zeichen. Diese Kodierung hat 100 8 = 64 verschiedene Zeichen. Das Oktalsystem besteht aus 8 verschiedenen Ziffern. Das Programm muss 160 Kopien jeder Ziffer enthalten, damit 8 × 160 = 1280 Ziffern möglich sind.
Ich behalte 160 Stellen in $s
und die anderen 1120 Stellen in $t
. Ich beginne mit einem Programm, das kein Quine ist, sondern nur die Zuweisungen für $s
und $t
für den nächsten Lauf ausgibt . Das ist es:
$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))
ist eine Zuweisung zu einer leeren Liste von Variablen. Ich nehme diesen Trick aus dem Kontext-Tutorial bei PerlMonks . Erzwingt den Listenkontext für den Match-Operator =~
. Im skalaren Kontext wäre die Übereinstimmung wahr oder falsch, und ich bräuchte eine Schleife, $i++ while ($s =~ /$_/g)
um die Übereinstimmungen zu zählen. Im Listenkontext $s =~ /$_/g
ist eine Liste von Übereinstimmungen. Ich habe diese Liste in den skalaren Kontext einer Subtraktion gestellt, sodass Perl die Listenelemente zählt.
Um eine Quine zu machen, nehme ich die Form $_=q{print"\$_=q{$_};eval"};eval
der Perl-Quines bei Rosetta Code . Dieser ordnet einen String q{...}
zu $_
und ruft dann auf eval
, sodass ich meinen Code in einem String haben und ihn auch ausführen kann. Mein Programm wird zu einem Quine, wenn ich meine drittletzten Zeilen in $_=q{
und einbinde };eval
und meine letzten print
in ändern print "\$s = '$s';\n\$t = '$t';\n\$_=q{$_};eval"
.
Schließlich spiele ich mein Programm ab, indem ich die erste Zuweisung $t
in einen Kommentar ändere und zusätzliche Zeichen entferne.
Dies hat 1522 ASCII-Zeichen (einschließlich 1280 permutierten Oktalzeichen).
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
Der Umstieg auf Binär
In den Kommentaren bemerkte Dennis, dass 960 permutierte Binärziffern weniger als 1280 Oktalziffern sind. Also habe ich die Anzahl der permutierten Stellen für jede Basis von 2 bis 16 grafisch dargestellt.
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)
Obwohl die Basis 8 ein lokales Minimum darstellt, ergeben die Basen 2 und 3 und 4 mit 960 permutierten Stellen die beste Basis. Für Code-Golf ist Basis 2 am besten geeignet, da Perl Konvertierungen für Basis 2 hat.
Durch Ersetzen von 1280 Oktalzeichen durch 960 Binärzeichen werden 320 Zeichen gespeichert.
Das Umschalten des Codes von Oktal auf Binär kostet 8 Zeichen:
- Änderung
oct
der oct'0b'.$_
Kosten 7.
- Änderung
/../g
der /.{6}/g
Kosten 2.
- Wechsel
"%02o"
zu "% 06b" `kostet 0.
- Wechsel
160
zu 480
Kosten 0.
0..7
Zum 0,1
Speichern wechseln 1.
Ich habe einige Perl-Golftipps gelernt . Sie speichern 14 Zeichen:
- Wechsel
'A'..'Z','a'..'z','0'..'9'
an A..Z,a..z,0..9
, mit Barewords und nackten Zahlen, spart 12 Zeichen.
- Ändern,
"\n"
um $/
2 Zeichen zu speichern.
Ich spare 3 Zeichen, indem ich den #$t
Kommentar an das Ende der Datei verschiebe. Das entfernt die neue Zeile, die den Kommentar beendet, und ein Literal \n
in dem Quine.
Diese Änderungen speichern insgesamt 329 Zeichen und reduzieren meine Punktzahl von 1428 auf 1099.