Včera večer byla uvolněna jako každý rok na přelomu května a června nová verze perlu 5. Co se za poslední rok změnilo?
2.6.2015 08:00 | Jiří Václavík | přečteno 19640×
Perl 5 pokračuje, možná trochu ve stínu perlu 6, jehož interpret v první stabilní verzi by snad již letos opravdu měl vyjít, poklidně ve vývoji. Cílem dnešního článku je postupně projít některé změny, které se za poslední rok od verzí řady 5.20 udály a trochu čtenáře zasvětit do toho, co se s perlem vlastně v poslední době děje.
Na začátku je třeba podotknout, že vývoj perlu 5 běží nezávisle na perlu 6, ačkoliv jsou velmi často změny v perlu 5 perlem 6 inspirovány. Jde ale o dva různé jazyky a dva různé týmy lidí, kteří se na jejich vývoji podílejí.
Hlavním člověkem a release managerem posledních major verzí je již od řady 5.16 Ricardo Signes.
Na distribuční balíčky nové verze perlu si asi budeme muset chvíli počkat, ale kdo by chtěl, může si novou verzi nainstalovat ze zdrojových kódů.
Doporučit lze instalaci pomocí nástroje perlbrew
, který je přímo určen pro správu několika verzí perlu zároveň a nevyžaduje žádné zásahy do stávajícího systému, takže nový perl může testovat opravdu každý. Navíc pomocí něj lze instalovat také vývojové verze, release candidate distribuce atd.
Instalace perlbrew
je velmi přímočará. Postupujme pomocí návodu na perlbrew.pl. Příkaz
> wget -O - http://install.perlbrew.pl | bash
provede instalaci samotného perlbrew
. Pro instalaci konkrétní verze perlu do perlbrew
pak zavoláme
> perlbrew install perl-5.22.0
Dále můžeme zvolit implicitní verzi perlu pro stávající sezení
> perlbrew switch perl-5.22.0
a nebo místo toho volat všechny příkazy s prefixem
> perlbrew exec --with perl-5.22.0 [příkaz]
Tedy například:
> perlbrew exec --with perl-5.22.0 perl program.pl arg1 arg2
My budeme dále v tomto článku používat alias perl5.22.0
:
> alias perl5.22.0="perlbrew exec --with perl-5.22.0 perl"
V posledních letech se ustálila linuxovým jádrem inspirovaná pravidla pro vydávání nových verzí perlu 5:
Pojďme se tedy podívat na konkrétní změny. Zde jsou vybrány pouze některé, pokud možno ty zajímavější. Kompletní přehled je v dokumentu perldelta, který je součástí distribuce. Dokument perldelta má zčásti formální syntaxi a existují nástroje, díky kterým dostaneme rozdíl mezi libovolnými verzemi perlu. Je tedy v podstatě sám o sobě jednoduchým správcem verzí.
<<>>
Operátor <>
se používal jako zkratka pro <ARGV>
, kde ARGV
byl implicitně definovaný ovladač souborů předaných na příkazovém řádku, pokud byly definované; nebo případně vstup z STDIN
. Díky této konstrukci lze v perlu velmi snadno předané soubory číst.
Tento způsob čtení pomocí operátoru <>
má ovšem jeden nedostatek - nelze ho použít pro všechny soubory. Ve skutečnosti se totiž argumenty příkazového řádku ještě interpretují, což v některých okrajových případech může způsobit pro někoho možná nečekané chování. Příklady interpretovaných znaků je |
, >
, >>
apod.
Pojďme se podívat podrobněji o co jde. Nejprve vytvořme soubor s trochu obskurním jménem echo SOUCAST_JMENA_SOUBORU |
a uložme do něj nějaký obsah.
> echo 'OBSAH SOUBORU' > 'echo SOUCAST_JMENA_SOUBORU |'
Nyní je již jasné, kam míříme. Zkusme nyní číst pomocí operátorů <>
a <<>>
a pozorujme rozdíl:
> perl5.22.0 -e 'while (<>) {print;}' 'echo SOUCAST_JMENA_SOUBORU |'
SOUCAST_JMENA_SOUBORU
> perl5.22.0 -e 'while (<<>>) {print;}' 'echo SOUCAST_JMENA_SOUBORU |'
OBSAH SOUBORU
>
V prvním případě došlo k interpretaci argumentu, zatímco v druhém se nic takového nestalo.
Zájemci si mohou zkusit totéž se souborem začínajícím na >
a smazat tak obsah potenciálně existujícího souboru.
Otázkou je, jak řešit tento problém v dřívejších verzích perlu. K pochopení by mohla napomoci souvislost s funkcí open
. Z podobného důvodu totiž kdysi došlo ke vzniku tříparametrové varianty této funkce, kdy se odděluje mód a název souboru. Jak byste například u dvouparametrové varianty četli soubor začínající na >
? U open
je problém ještě zřejmější, protože se tam speciální znaky (narozdíl od jejich výskytu u jmen souborů) běžné používají. Proto ostatně změna u open
nastala již u verze 5.6. Ale zpět k řešení problému u <>
- pomoci by nám zde měla právě tříparametrová funkce open
. Ta v podstatě přesně vystihuje rozdíl mezi <>
a <<>>
. Následující kód s tříparametrovou funkcí open
hezky popisuje změnu <<>>
oproti <>
:
FILE:
for my $file (@ARGV) {
open my $fh, '<', $file || next FILE;
while(my $line = <$fh>){
print $line;
}
}
Je-li nějaká funkcionalita experimentální, pak to jednoduše znamená to, že bychom měli být obezřetní, neboť se její chování může změnit. Experimentální featury podle interních pravidel vydávání musí zůstat nezměněny alespoň 2 roky (nepočítají se samozřejmě změny v dokumentaci, refactoring kódu, znění chybových hlášek atd.). Po této lhůtě lze diskutovat o zrušení příznaku "experimentální".
I když víme, že se chování nezmění (tj. hlavně proto. že používáme starou verzi perlu a v nové již featura experimentální není), musíme to reflektovat v kódu. Jednotlivé featury mají svá jména a před použitím bychom měli zavolat pragmu feature:
use feature qw(featura);
Takto ovšem použití dané featury nadále generuje varování. Chceme-li ho umlčet, přidáme ještě tento řádek:
no warnings 'experimental::featura';
Je ovšem na důkladné zvážení, zda se používání jakékoliv experimentální featury vyplatí a nestojí za to počkat si dva roky až bude syntaxe stabilní. Pokud jde daný problém vyřešit jinak a není s tímto řešením jiný problém, pak bychom se pro něj měli rozhodnout, protože takové řešení je čistější.
Příkladem experimentální featury jsou od řady 5.20 signatury v definicích podprogramů místo používání proměnné @_
.
Aliasem myslíme proměnnou, která ukazuje na stejné místo v paměti jako nějaká jiná proměnná.
Aliasing je experimentální featura, takže platí vše z předchozího odstavce. Přibližme si o co v případě aliasingu půjde. Jelikož experimentální featury je třeba zapnout, zadejme
use feature qw(refaliasing);
# no warnings 'experimental::refaliasing';
Je možná následující syntaxe pro vytvoření aliasu proměnné, například:
\%new_hash = \%old_hash;
is(\%new_hash, \%old_hash, "oba hashe ukazují na stejné místo v paměti");
\&new_code = \&old_code;
is(\&new_code, \&old_code, "oba podprogramy jsou též aliasy");
Díky této vlastnosti je možné při procházení složitější datové struktury získat přímo pole místo pouhého odkazu na něj. Konkrétně tedy místo
for my $hashref (@list_of_hashes) {
process(%$hashref);
}
můžeme napsat o něco expresivnější
for \my %hash (@list_of_hashes) {
process(%hash);
}
Detailnější možnosti jsou v dokumentu Nový perl přichází s podporou aktuální verze Unicode 7.0.
Rozdíly si můžeme krátce demonstrovat. Představme si, že máme následující tokenizery:
Budeme chtít zobrazit, kde jsou boundaries v nějakém textu:
Z výsledku je pěkně vidět, jak jednotlivé boundaries fungují:
Už jen stručně další změny v regulárních výrazech:
Jedním z hlavních důvodů, proč by se nové featury měly přidávat do jazyka jen velmi opatrně po dlouhé diskuzi je nutnost zpětné kompatibility, na kterou se v perlu velmi striktně dbá. Jakmile se totiž do široce používaného jazyka zavede funkcionalita, která není žádoucí (např. z důvodu matoucí interpretace, narušení jiných a třeba i budoucích a zatím neplánovaných featur), je to obrovský problém, protože cesta zpátky je téměř nemožná. Čištění jazyka nicméně v omezené míře probíhá, i když nějaké zásadní změny se z výše uvedeného důvodu dít nemohou. (Od toho bude perl 6.)
Podle Richarda Signese bude v jazyce každá funkcionalita označená jako deprecated minimálně další dva roky (výjimečně i pouze rok, ale to se ještě nestalo) a bude generovat varování. Potom může (a taky nemusí) dojít jejímu odstranění. Několik deprecated syntaktických konstrukcí (některé byly deprecated i 20 let) bylo nyní odstraněno:
Obecně lze říci, že cokoliv deprecated nemá vůbec žádný smysl používat, protože to může být v budoucnu odstraněno. Navíc je stejně velmi pravděpodobné, že takováto featura bude v silném rozporu s Perl Best Practices, jejichž striktní používání je v moderním perlu naprosto nezbytné.
Podívejme se jen pro zajímavost a ilustraci, co v perlu vlastně bylo, na několik featur, které jsou nově odstraněny. Všimněme si mimochodem, jak dramaticky všechny následující kostrukce snižovaly čitelnost kódu.
Verze pro Windows podporuje konstrukce s rourou u tříparametrové funkce Odstraněn z core modulů byl kdysi nepostradatelný modul CGI. V době MVC a jiných frameworků a šablonovacích systémů již není třeba. Dále byl odstraněn modul Module::Build z důvodu nepohodlných závislostí ( Všechny bývalé core moduly jsou samozřejmě nadále na CPANu.
Unicode
Regulární výrazy
Asi nejzajímavější nová věc je, že máme k dispozici rozšířenou syntaxi pro boundaries. \b
standardně znamená kotvu pro hranici slova. Je trochu ošemetné mít v jazyce něco závislého na slově, protože sama definice slova je velmi nejasná a napříč jazyky se může diametrálně lišit. Totéž platí o větách, jejichž koncept navíc v mluveném jazyce zcela ztrácí smysl. Přesto jsou boundaries pro jednoduché tokenizátory poměrně dobře funkční. Lingvistům nyní udělají velkou radost následující nové kotvy:
\b{sb}
- hranice věty
\b{wb}
- hranice slova - funguje skoro jako staré \b
, ale reflektuje něco více z přirozeného jazyka. Například ví, že apostrof se může vyskytnout i uvnitř slova (výhodnost záleží na konkrétním použití)
\b{gcb}
- matchuje hranice znaku tak, jak ho vidí člověk (ve skutečnosti může zápis několika symbolů tvořit jen jeden grafém - některé znaky například pouze přidávají nějaký symbol k jinému znaku; například ze dvou znaků P\x{307}
vznikne P s tečkou) - více na perldoc perlbackslash
my $tokenizers = {
b => qr{(?:\b)}xms,
sb => qr{(?:\b{sb})}xms,
wb => qr{(?:\b{wb})}xms,
gcb => qr{(?:\b{gcb})}xms,
};
my $text = "Aaaa a aaaa aaaaa aa. B. Cc'c ccc'ccc c\"c. D\x{307}d\x{307}.";
for my $boundary_type (keys %$tokenizers) {
my $text_copy = $text;
$text_copy =~ s{$tokenizers->{$boundary_type}}{|}xmsg;
print "$boundary_type: $text_copy\n";
}
b: |Aaaa| |a| |aaaa| |aaaaa| |aa|. |B|. |Cc|'|c| |ccc|'|ccc| |c|"|c|. |Ḋḋ|.
wb: |Aaaa| |a| |aaaa| |aaaaa| |aa|.| |B|.| |Cc'c| |ccc'ccc| |c|"|c|.| |Ḋḋ|.|
sb: |Aaaa a aaaa aaaaa aa. |B. |Cc'c ccc'ccc c"c. |Ḋḋ.|
gcb: |A|a|a|a| |a| |a|a|a|a| |a|a|a|a|a| |a|a|.| |B|.| |C|c|'|c| |c|c|c|'|c|c|c| |c|"|c|.| |Ḋ|ḋ|.|
/n
pro potlačení extrakce pomocí závorek
use re 'strict'
upozorní na potenciální chyby
/x
je úplnější, nyní zahrnuje další typy mezer
\c
funguje pouze pro tisknutelné znaky
/x
lze uvést pouze jednou
Mazání deprecated featur
Odstraněná funkcionalita
?regex?
jakožto regulární výraz pro jednorázové porovnání nebude fungovat. Pokud přidáme operátor m
, tj. m?regex?
tak bude validní nadále - to však nemění nic na tom, že používat takto nekonvenční zápis by se vůbec nemělo. Doporučená základní syntaxe pro jednorázové porovnávání resp. nahrazování regulárních výrazů je m{regex}xms
resp. s{regex}{string}xms
, případně ještě m/regex/xms
resp. s/regex/string/xms
.
%hash->{$key}
místo $hash{$key}
již není možné, objeví se chybová hláška Can't use a hash as a reference"
@list->[$index]
místo $list[$index]
.
my @list = (1, 2);
push list, 3, 4;
my %hash = (a => 1);
@keys = keys hash;
defined(@list)
a defined(%hash)
. Nyní se objeví chybová hláška s návrhem Maybe you should just omit the defined()?
. V případě testování na prázdnost pole stačí defined
vynechat a u hashů ještě přidat keys
.
Změny specifické pro platformy
open
, což doposud možné nebylo:
open my $fh, '-|', @list;
open my $fh, '|-', @list;
Změny v modulech
automake
).
Další změny stručně
&.
, |.
, ^.
, ~.
. To proto, že perl místo typování rozlišuje často typy operandů pomocí operátorů. Staré bitové operátory vyžadují čísla. Takové chování bude konzistentnější s ostatními operátory.
close
nastaví chybový kód do proměnné $!
.
UNIVERSAL
pomocí use
.
my()
lze zadávat repetitive operátor, použití by mohlo vypadat například takto: my((undef) x 5, $year) = localtime(time());
.
0x1.23p-4
(převody lze vyzkoušet pomocí printf("%a", $number)
)
Další zdroje informací
Článek je vydán bez honoráře.