Internet Info, s.r.o. Lupa Měšec Podnikatel Root Zdroják DigiZone Slunečnice Vitalia TopDrive KupDnes Navrcholu NovýTarif Dobrý web Weblogy Woko Jagg Computer.cz SK: MojeLinky

Hlavní navigace

Perličky: práce se seznamy

Volné pokračování seriálu Perličky z let 2001 a 2002 o programovacím jazyce Perl je určeno především programátorům pokročilým v obecné teorii programování, případně jiném jazyce a zároveň začátečníkům až mírně pokročilým v jazyce Perl. V dnešním díle si ukážeme, jak Perl nahlíží na seznamy, pole a hashe.

Tweetni to Twitter Jaggni to! Jagg Del.icio.us Delicious

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$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.

davame_internetu_obsah
       
#!/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.

Školení: Linux – Firemní server

Na třídenním školení se naučíte nainstalovat a spravovat kompletní linuxový server do Vaší firmy se všemi základními službami, které potřebujete pro provoz Vaší sítě, firemních emailů a webových stránek.

Podrobnější informace a přihláška

Ohodnoťte jako ve škole:
Průměrná známka 2,72

Přehled názorů

Pěkný článek
Keson 8. 2. 2008 01:20
Nový
└ 
Re: Pěkný článek
Michal Svoboda 8. 2. 2008 14:32
Nový
 
└ 
Re: Pěkný článek
lzap 8. 2. 2008 23:14
Nový
unless defined $max and $max > 0;
Miroslav Suchý 8. 2. 2008 11:02
Nový
├ 
Re: unless defined $max and $max > 0;
kvr 8. 2. 2008 12:02
Nový
│
└ 
Re: unless defined $max and $max > 0;
Michal Svoboda 8. 2. 2008 14:31
Nový
│
 
└ 
Re: unless defined $max and $max > 0;
Miroslav Suchý 8. 2. 2008 20:09
Nový
│
 
 
└ 
Re: unless defined $max and $max > 0;
BLEK. 12. 2. 2008 20:05
Nový
│
 
 
 
└ 
Re: unless defined $max and $max > 0;
Petr 13. 2. 2008 18:30
Nový
│
 
 
 
 
└ 
Re: unless defined $max and $max > 0;
Michal Svoboda 13. 2. 2008 21:43
Nový
└ 
Re: unless defined $max and $max > 0;
valy 8. 2. 2008 12:04
Nový
hezky
anonymní uživatel 8. 2. 2008 18:59
Nový
Pěkné
tukan 9. 2. 2008 03:48
Nový
├ 
Re: Pěkné
lzap 10. 2. 2008 10:33
Nový
└ 
Re: Pěkné
Michal Svoboda 10. 2. 2008 13:31
Nový
 
└ 
Re: Pěkné
tukan 10. 2. 2008 15:39
Nový
Dalším častým operátorem jsou dvě tečky .., které umí &#8222;počítat po…
anonymní uživatel 11. 2. 2008 11:24
Nový
└ 
Re: Dalším častým operátorem jsou dvě tečky .., které umí &#8222;počítat po…
Michal Svoboda 11. 2. 2008 16:07
Nový
Naucte
Bubla 14. 2. 2008 14:55
Nový
└ 
Re: Naucte
Michal Svoboda 14. 2. 2008 20:52
Nový
       

Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.

Zasílat nově přidané příspěvky e-mailem