Perl, 438 291 znaków
Zainspirowany użytku Jeff Burdges męska kompresji DEFLATE , skompresowanego kodu Ventero za Ruby i użytkowania JB dnia Lingua :: PL :: Liczb , udało mi się ściskać moją pozycję w dół do 291 znaków (dobrze, bajty) włącznie z kodem dekompresji. Ponieważ program zawiera niektóre niedrukowalne znaki, podałem go w formacie MIME Base64 :
dXNlIENvbXByZXNzOjpabGliO2V2YWwgdW5jb21wcmVzcyAneNolkMFqAkEMhu8+RVgELdaIXmXB
S2/FFyhF4k7cHTqTsclMZd++M3pJvo+QH5JiDJ9exkKrj/PqXOKV1bod77qj9b2UeGBZ7w/bpd9s
3rCDruf3uWtwS3qS/vfROy0xsho+oWbB3d+b19YsJHWGhIHp5eQ8GzqSoWkk/xxHH36a24OkuT38
K21kNm77ND81BceCWtlgoBAq4NWrM7gpyzDhxGKQi+bA6NIfG5K4/mg0d0kgTwwdvi67JHVeKKyX
l3acoxnSDYZJveVIBnGGrIUh1BQYqZacIDKc5Gvpt1vEk3wT3EmzejcyeIGqTApZmRftR7BH3B8W
/5Aze7In
Aby odkodować program, możesz użyć następującego skryptu Perl pomocnika:
use MIME::Base64;
print decode_base64 $_ while <>;
Zapisz wynik w pliku o nazwie 12days.pli uruchom go perl -M5.01 12days.pl. Jak wspomniano, aby kod działał , musisz mieć zainstalowany moduł Lingua :: EN :: Numbers .
Jeśli się zastanawiasz, czytelna część kodu wygląda tak:
use Compress::Zlib;eval uncompress '...'
gdzie ...oznacza 254 bajty RFC 1950 skompresowanego kodu Perl . Kod nieskompresowany ma 361 znaków i wygląda następująco:
use Lingua'EN'Numbers"/e/";s==num2en(12-$i++)." "=e,y"." "for@n=qw=drummers.drumming pipers.piping lords.a.leaping ladies.dancing maids.a.milking swans.a.swimming geese.a.laying golden.rings calling.birds french.hens turtle.doves.and=;say"on the ".num2en_ordinal($_)." day of christmas my true love gave to me @n[$i--..@n]a partridge in a pear tree
"for 1..12
Pisanie tego kodu było dziwnym rodzajem ćwiczenia w golfa: okazuje się, że maksymalizowanie powtórzeń i minimalizowanie liczby użytych znaków są o wiele ważniejsze niż minimalizowanie liczby znaków surowych, gdy odpowiednią miarą jest rozmiar po kompresji .
Aby wycisnąć kilka ostatnich znaków, napisałem prosty program, aby wypróbować małe odmiany tego kodu, aby znaleźć ten, który najlepiej się kompresuje. Do kompresji użyłem narzędzia KZIP Kena Silvermana , które zwykle daje lepsze racje kompresji (kosztem prędkości) niż standardowe Zlib, nawet przy maksymalnych ustawieniach kompresji. Oczywiście, ponieważ KZIP tworzy tylko archiwa ZIP, musiałem wyodrębnić surowy strumień DEFLATE z archiwum i zawinąć go w nagłówek RFC 1950 i sumę kontrolną. Oto kod, którego użyłem do tego:
use Compress::Zlib;
use 5.010;
@c = qw(e i n s);
@q = qw( " );
@p = qw( = @ ; , );
@n = ('\n',"\n");
$best = 999;
for$A(qw(e n .)){ for$B(@q){ for$C(@q,@p){ for$D(@p){ for$E(@q,@p){ for$F(qw(- _ . N E)){ for$G("-","-"eq$F?():$F){ for$H(@c){ for$I(@c,@p){ for$N(@n){ for$X(11,"\@$I"){ for$Y('$"','" "',$F=~/\w/?$F:()){ for$Z('".num2en_ordinal($_)."'){
$M="Lingua'EN'Numbers";
$code = q!use MB/A/B;sDDnum2en(12-$H++).YDe,yCFC Cfor@I=qwEdrummersFdrumming pipersFpiping lordsGaGleaping ladiesFdancing maidsGaGmilking swansGaGswimming geeseGaGlaying goldenFrings callingFbirds frenchFhens turtleFdovesFandE;say"on the Z day of christmas my true love gave to me @I[$H--..X]a partridge in a pear treeN"for 1..12!.$/;
$code =~ s/[A-Z]/${$&}/g;
open PL, ">12days.pl" and print PL $code and close PL or die $!;
$output = `kzipmix-20091108-linux/kzip -b0 -y 12days.pl.zip 12days.pl`;
($len) = ($output =~ /KSflating\s+(\d\d\d)/) or die $output;
open ZIP, "<12days.pl.zip" and $zip = join("", <ZIP>) and close ZIP or die $!;
($dfl) = ($zip =~ /12days\.pl(.{$len})/s) or die "Z $len: $code";
$dfl = "x\xDA$dfl" . pack N, adler32($code);
$dfl =~ s/\\(?=[\\'])|'/\\$&/g;
next if $best <= length $dfl;
$best = length $dfl;
$bestcode = $code;
warn "$A$B$C$D$E$F$G$H$I $X $Y $best: $bestcode\n";
open PL, ">12days_best.pl" and print PL "use Compress::Zlib;eval uncompress '$dfl'" and close PL or die $!;
}}}}}}
print STDERR "$A$B$C$D$E$F\r";
}}}}}}}
Jeśli to wygląda jak okropny kluge, to dlatego, że właśnie tak jest.
Dla historycznego zainteresowania, oto moje oryginalne rozwiązanie 438-char, które generuje ładniejsze wyjście, w tym podział wierszy i interpunkcję:
y/_/ /,s/G/ing/for@l=qw(twelve_drummers_drummG eleven_pipers_pipG ten_lords-a-leapG nine_ladies_dancG eight_maids-a-milkG seven_swans-a-swimmG six_geese-a-layG five_golden_rGs four_callG_birds three_french_hens two_turtle_doves);s/e?t? .*/th/,s/vt/ft/for@n=@l;@n[9..11]=qw(third second first);say map("\u$_,\n","\nOn the $n[11-$_] day of Christmas,\nMy true love gave to me",@l[-$_..-1]),$_?"And a":A," partridge in a pear tree."for 0..11
Najważniejsze w tej wersji para wyrażeń regularnych s/e?t? .*/th/,s/vt/ft/ , które konstruują liczby porządkowe dla 4 do 12 z kardynałów na początku linii prezentu.
Ten kod można oczywiście również skompresować przy użyciu sztuczki Zlib opisanej powyżej, ale okazuje się, że po prostu kompresja danych wyjściowych jest bardziej wydajna, co daje następujący 338-bajtowy program (ponownie w formacie Base64):
dXNlIENvbXByZXNzOjpabGliO3NheSB1bmNvbXByZXNzICd42uWTwU7DMAyG730KP8DGOyA0bsCB
vYBp3MYicSo7W9e3xx3ijCIQDHZIUjn683+/k3ZPAjUSDKxWIeACZYC7qGw1o226hwWqHghSORKM
6FMtkGnT3cKEWpXDSMACCBOhQlWim+7jUKO+SGg5dT8XqAetiSD4nrmPBMDPvXywtllF18OgJH2E
SGJfcR+Ky2KL/b0roMeUWEZ4cXb7biQeGol4LZQUSECdyn4A0vjUBvnMXCcYiYy2uE24ONcvgdOR
pBF9lYDNKObwNnPOTnc5kYjH2JZotyogI4c1Ueb06myXH1S48eYeWbyKgclcJr2D/dnwtfXZ7km8
qOeUiXBysP/VEUrt//LurIGJXCdSWxeHu4JW1ZnS0Ph8XOKloIecSe39w/murYdvbRU+Qyc=
Mam również 312-bajtowe archiwum gzip tekstów, zbudowane z tego samego strumienia DEFLATE. Przypuszczam, że można to nazwać „skryptem Zcat”. :)