Perl, 438 291 символ
Вдохновленный использованием Джеффом Берджесом сжатия DEFLATE , сжатого кода Ventero Ruby и использованием JB Lingua :: EN :: Numbers , мне удалось сжать мою запись до 291 символа (ну, в байтах), включая код распаковки. Поскольку программа содержит некоторые непечатаемые символы, я предоставил ее в формате MIME Base64 :
dXNlIENvbXByZXNzOjpabGliO2V2YWwgdW5jb21wcmVzcyAneNolkMFqAkEMhu8+RVgELdaIXmXB
S2/FFyhF4k7cHTqTsclMZd++M3pJvo+QH5JiDJ9exkKrj/PqXOKV1bod77qj9b2UeGBZ7w/bpd9s
3rCDruf3uWtwS3qS/vfROy0xsho+oWbB3d+b19YsJHWGhIHp5eQ8GzqSoWkk/xxHH36a24OkuT38
K21kNm77ND81BceCWtlgoBAq4NWrM7gpyzDhxGKQi+bA6NIfG5K4/mg0d0kgTwwdvi67JHVeKKyX
l3acoxnSDYZJveVIBnGGrIUh1BQYqZacIDKc5Gvpt1vEk3wT3EmzejcyeIGqTApZmRftR7BH3B8W
/5Aze7In
Чтобы отменить код программы, вы можете использовать следующий вспомогательный скрипт Perl:
use MIME::Base64;
print decode_base64 $_ while <>;
Сохраните вывод в файле с именем 12days.pl
и запустите его perl -M5.01 12days.pl
. Как уже отмечалось, для работы кода необходимо установить модуль Lingua :: EN :: Numbers .
Если вам интересно, читаемая часть кода выглядит просто так:
use Compress::Zlib;eval uncompress '...'
где ...
обозначает 254 байта сжатого кода Perl RFC 1950 . Несжатый код имеет длину 361 символ и выглядит следующим образом:
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
Написание этого кода было странным видом игры в гольф: оказывается, максимизация повторения и минимизация количества используемых различных символов гораздо важнее, чем минимизация необработанного числа символов, когда соответствующая метрика имеет размер после сжатия .
Чтобы выжать последние несколько символов, я написал простую программу, чтобы попробовать небольшие варианты этого кода, чтобы найти тот, который сжимает лучше всего. Для сжатия я использовал программу KZIP Кена Сильвермана , которая обычно дает лучшие коэффициенты сжатия (за счет скорости), чем стандартный Zlib, даже при максимальных настройках сжатия. Конечно, поскольку KZIP создает только ZIP-архивы, мне пришлось извлечь из архива необработанный поток DEFLATE и обернуть его в заголовок и контрольную сумму RFC 1950. Вот код, который я использовал для этого:
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";
}}}}}}}
Если это похоже на ужасный клуг, то это потому, что это именно то, что есть.
Для исторического интереса вот мое оригинальное решение с 438 символами, которое генерирует более хороший вывод, включая разрывы строк и пунктуацию:
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
Основные моменты этой версии - пара регулярных выражений s/e?t? .*/th/,s/vt/ft/
, которые строят порядковые числа от 4 до 12 из кардиналов в начале линий подарков.
Этот код, конечно, также может быть сжат с использованием описанного выше трюка Zlib, но оказывается, что простое сжатие вывода более эффективно, в результате получается следующая 338-байтовая программа (опять же в формате Base64):
dXNlIENvbXByZXNzOjpabGliO3NheSB1bmNvbXByZXNzICd42uWTwU7DMAyG730KP8DGOyA0bsCB
vYBp3MYicSo7W9e3xx3ijCIQDHZIUjn683+/k3ZPAjUSDKxWIeACZYC7qGw1o226hwWqHghSORKM
6FMtkGnT3cKEWpXDSMACCBOhQlWim+7jUKO+SGg5dT8XqAetiSD4nrmPBMDPvXywtllF18OgJH2E
SGJfcR+Ky2KL/b0roMeUWEZ4cXb7biQeGol4LZQUSECdyn4A0vjUBvnMXCcYiYy2uE24ONcvgdOR
pBF9lYDNKObwNnPOTnc5kYjH2JZotyogI4c1Ueb06myXH1S48eYeWbyKgclcJr2D/dnwtfXZ7km8
qOeUiXBysP/VEUrt//LurIGJXCdSWxeHu4JW1ZnS0Ph8XOKloIecSe39w/murYdvbRU+Qyc=
У меня также есть 312-байтовый gzip-архив текстов песен, созданный из того же потока DEFLATE. Я полагаю, вы могли бы назвать это «сценарием zcat». :)