Seznamy, řezy a pole
Perlovský seznam je vektor obsahující nula nebo více skalárních veličin. Seznam lze zapsat pomocí klasické závorkové konstrukce, často se však používá operátor qw()
nebo qw//
(což ušetří zmáčknutí SHIFTu). Operátor qw
zachází se svým vnitřkem, jako by byl v jednoduchých uvozovkách a vytvoří seznam rozsekáním podle prázdných znaků. Dalším častým operátorem jsou dvě tečky ..
, které umí „počítat po jednom“.
$a = 5; (1, $a, "f") # (1, 5, "f") (1, ($a, "f")) # totéž qw/raz dva tři/ # ('raz', 'dva', 'tři') 2 .. 5 # (2, 3, 4, 5) "A" .. "C" # ("A", "B", "C") () # prázdný seznam, velice užitečný
K prvkům seznamu lze přistupovat pomocí indexů. Indexem může být skalár nebo opět seznam. Skalární indexy začínají na hodnotě nastavené $[
, což je obvykle 0. Indexy menší než toto číslo značí indexování seznamu od jeho konce. V případě indexování seznamu seznamem vytvoříme takzvaný řez, což je seznam obsahující prvky původního seznamu na zadaných indexech. Indexy řezu začínají opět od hodnoty $[
.
(1, $a, "f")[0] # 1 (1, $a, "f")[-1] # "f" qw/ahoj děti/[1] # 'děti' (0 .. 9)[3 .. 5] # ('3', '4', '5') ((0 .. 9)[3 .. 5])[-2] # 4 qw/nastoupil vystoupil/[1, 0] # ('vystoupil', 'nastoupil')
Perlovské pole (zapsáno @a
) pomocí symbolu @
pouze říká, abychom na symbol a
nahlíželi jako na identifikátor seznamu, podobně jako bychom to česky vyjádřili pomocí přípony v plurálu („áčka“). Je důležité si uvědomit, že tímto získáváme seznam se všemi funkcemi všude tam, kde se objevuje zápis @a
. Je to poněkud odlišný způsob nahlížení na pole, než jaký se používá v jiných moderních programovacích jazycích.
@a = qw/skákal pes přes oves/; @b = qw/přes zelenou louku/; @c = (2, -1 .. 1); (@a, @b)[@c] # přes louku skákal pes
Seznamy a hashe
Perlovský hash se má se seznamy celkem rád. Lze si opět představit, že zápis %a
znamená „áčka, na která se nahlíží jako na páry (klíč, hodnota)“. Hashe se inicializují seznamem, operátor =>
převážně slouží k lepší čitelnosti kódu. Hashe lze indexovat vyhledáním klíče pomocí složených závorek. Česky bychom řekli „hodnota klíče v seznamu“( $a{'klíč'}
). Jako index lze také použít seznam a tak vytvářet řezy. V tom případě však musíme prefixovat symbolem @
, neboť řez je seznam. Dále lze hashe umístit do seznamu nebo přiřadit do pole a takto získat zpět seznam párů (klíč, hodnota). Pořadí párů však bude náhodné.
$ENV{'PATH'} # '/bin:…' @ENV{qw/HOME USER/} # všimněte si symbolu @, více indexy vytváříme seznam %h = @a; # ('skákal' => 'pes', 'přes' => 'oves') $h{'přes'} # 'oves' %k = (%h, @b); # index 'přes' se přepíše na 'přes' => 'zelenou' # a přidá se 'louku' => undef @u = 'A' .. 'Z'; %v = @u; # 'A' => 'B', 'C' => 'D', … @w = %v; # pořadí prvků je jiné než v @u, asociace párů však zůstane
Struktury a pojmenované parametry
Hashe se používají nejen čistě pro účely hashování, ale často také tam, kde by stála konstrukce struct
v C.
%zaznam = ( 'jméno' => 'František Koudelka', 'oddíl' => 'STS Chvojkovice-Brod' );
(Jak vytvořit pole takových záznamů si ukážeme v příštím díle.)
Příbuznost hashů a seznamů lze využít také jako náhražku pojmenovaných parametrů funkcí.
sub spojeni { my %parametry = @_; print "Spojuji se na '$parametry{'kam'}' jako '$parametry{'kdo'}'\n"; } # pak lze použít spojeni(kdo => 'pes', kam => 'okno'); spojeni(kam => 'díra', kdo => 'kočka');
Spolu s tím si lze dopřát i luxus parametrů s výchozími hodnotami. Lze to udělat klasicky pomocí operátoru or
, nebo využít fakt, že nová dvojice (klíč, hodnota) v seznamu přepíše starou.
sub spojeni { my %parametry = @_; $parametry{'kdo'} or $parametry{'kdo'} = $ENV{'USER'}; print "Spojuji se na '$parametry{'kam'}' jako '$parametry{'kdo'}'\n"; } # nebo sub spojeni { my %parametry = (kdo => $ENV{'USER'}, @_); print "Spojuji se na '$parametry{'kam'}' jako '$parametry{'kdo'}'\n"; } # pak lze zavolat spojeni(kam => 'domu');
Seznamové operátory
K často používaným seznamovým operátorům patří přiřazení a print
. V případě přiřazení lze do seznamu na levé straně napsat skalární proměnné, jejichž obsah se tímto přiřadí. Operátor print
použije k oddělení prvků seznamu hodnotu $,
.
($home, $user) = $ENV{qw/HOME USER/}; print "Babka má ", secti_jabka(), " jablek."; # obsah všech seznamů v dnešních příkladech lze rychle vypsat takto $, = " "; print @pole; # nebo (seznam) # nebo beze změny $, { local $, = " "; print @pole; }
K řazení prvků se používá operátor sort
. Řadit lze dle návratové hodnoty libovolné funkce (která je záporná, kladná, nebo nula), nicméně zajímavější je varianta s blokem kódu. V bloku jsou definované identifikátory $a
a $b
jako reference na dvě porovnávané hodnoty.
sort { $a cmp $b } qw/pes prase ping/; # seřadí jako řetězce sort (1..100); # také jako řetězce sort { $a - $b } (1..100); # jako čísla sort { $a <=> $b } (1..100); # také jako čísla sort { abs($a) <=> abs($b) } (-100..100); # jako čísla, bez znaménka
Chceme-li řadit podle nějaké složitější funkce, je vhodné si její hodnoty dopředu spočítat a řadit podle těchto hodnot.
$hodnoty{$_} = funkce($_) for @pole; @serazeno = sort { $hodnoty{$a} <=> $hodnoty{$b} } @pole; %hodnoty = undef;
(Toto lze provést poněkud elegantněji pomocí referencí a operátoru map
. O referencích ale příště.)
Zmiňovaný operátor map
umožňuje na seznamu provést libovolnou transformaci. Ve vykonávaném bloku přestavuje $_
odkaz na aktuálně zpracovávaný prvek. Na rozdíl od cyklu for
však můžeme prvky seznamu mazat nebo přidávat, jestliže posledním výrazem v bloku nevyhodnotíme skalár, ale prázdný seznam, respektive seznam s více hodnotami.
@mocniny = map { 2**$_ } (0 .. 8); # je zhruba ekvivalentní for (0 .. 8) { $mocniny[$_] = 2**$_; } @mala_pismenka = map { lc($_) } @slova; # výše uvedený cyklus # $hodnoty{$_} = funkce($_) for @pole # lze zapsat také takto %hodnoty = map { $_ => funkce($_) } @pole; # mazat prvky lze pomocí prázdného seznamu @vetsi_nez_10 = map { $_ > 10 ? $_ : () } (1 .. 100);
Operátor grep
je obdoba posledního příkladu na map
. Pokud vyhodnocovací blok vrátí pravdivou hodnotu, prvek je v seznamu ponechán, jinak je vymazán. Pokud je grep vyhodnocen jako skalár, vrátí počet prvků, které testem prošly.
@skryte_soubory = grep { $_ =~ /^\./ } @soubory; $autorizovan = grep { $jmeno eq $_ } @autorizovani_uzivatele;
Závěr
V dnešním díle jsme si ukázali některé mocnější techniky pro práci se seznamy, poli a hashi. Naše poznatky můžeme shrnout do následujícího prográmku.
#!/usr/bin/perl -w use strict; my ($max, $r) = (shift, 1); die "Použití: $0 <kladné číslo>\n" unless defined $max and $max > 0; $, = " "; my @faktorial = ( 1, map { $r *= $_; $r; } (1 .. $max) ); print @faktorial, "\n"; my @trojuhelnik = map { my $n = $_; map { $faktorial[$n] / $faktorial[$_] / $faktorial[$n - $_] } (1 .. $n); } (1 .. $max); print @trojuhelnik, "\n";
Generace pole faktoriálů spočívá ve vynásobení předchozí vypočtené hodnoty aktuálním prvkem ze sekvence 1 až $max
. Jako výjimka je první prvek (index 0) inicializován napevno jedničkou. Pascalův trojuhelník pak vyrobíme po řádcích (vnější map
), přičemž N-tý řádek obsahuje právě N prvků, které se vypočítají jako kombinační čísla (vnitřní map
).
V příštím díle se budeme zabývat převážně referencemi, způsobem, jak vybudovat složitější datové struktury a jejich použitím spolu s již probranými operátory.