LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> Perl (12) - Pole - základní operace

Perl Naučme se trochu orientovat v seznamovém kontextu a používat některé základní nástroje , mezi které patří třídění polí nebo operátor rozsahu.

1.8.2005 06:00 | Jiří Václavík | Články autora | přečteno 50978×

Seznam ve skalárním kontextu

Podobně jako mezi řetězci a čísly existují pravidla pro konverzi mezi seznamy a skaláry.

Různé funkce a operátory se chovají ke skalárům a seznamům jinak. Pracujeme-li se seznamem jako se skalární hodnotou (to lze, za chvíli přijde vysvětlení), Perl si s tím šalamounsky poradí. Takovýto seznam ve skalárním kontextu se tváří jako proměnná, jejíž hodnotou je počet prvků seznamu. Možná je to na první pohled neintuitivní řešení, ale této vlastnosti se skutečně často využívá. Na těchto řádcích kódu si ukažme, jak přesně to funguje:

@pole = qw(a b c d e f);
$skalar = @pole;
print $skalar;

Sledujme především druhý řádek. Protože je levý operand skalár, musí do něj být uložena skalární hodnota. Pravý operand tedy bude vyhodnocován také jako skalár, ať je čímkoliv. V našem případě se do proměnné $skalar přiřadí hodnota 6, což je počet prvků @pole.

Avšak pozor na následující příklad:

$skalar = (10, 20, 30); # POZOR!!!
print $skalar;

U seznamů, které nejsou poli, je to však jinak, než by se dalo podle dosavadních tvrzení čekat. Do $skalar je přiřazena poslední hodnota seznamu, tedy 30. Přitom, kdybychom přiřazovali do pole, získali bychom tříprvkové pole. Proč tomu tak je? Závorky zde Perl chápe jako operátor pro změnu priority a čárky jako klasický operátor čárky, který levý výraz vyhodnocuje a zapomíná. Malý test - co kdybychom neuvedli závorky?

Perl obsahuje speciální funkci, která vyjadřuje seznam ve skalárním kontextu. Funkce scalar převádí hodnotu v parametru na skalární. Je-li skalární, nic se neděje. Ale jde-li o pole, funkce vrátí počet jeho prvků.

@pole = qw(a b c d e f);
print scalar @pole; # vytisknuto je opět 6.

Teď si vezměme opačný případ. Přiřazení skalární proměnné do seznamu. Přiřazením vznikne jednoprvkové pole, jehož nultou hodnotou je hodnota na pravé straně přiřazení. Skalární hodnota se konvertuje v jednoprvkový seznam.

$skalar = 666;
@pole = $skalar; # index 0: hodnota 666

Funkce print s parametrem v seznamovém kontextu

Předáme-li funkci print pole, vytiskne hodnoty všech prvků pole.

@pole = qw(a b c d e f);
print @pole; # vytiskne abcdef.

Nemusíme předávat už vytvořené pole, ale třeba jen seznam hodnot, oddělených čárkami

print "a", "b", "c"; # vytiskne abc.

Samozřejmě i v print můžeme použít uzavření do uvozovek pomocí qw:

print qw(a b c); # vytiskne abc.

Speciální proměnné pro pole

Perl obsahuje desítky speciálních proměnných, jež mají různý význam. My si nyní představíme tři z nich, se kterými se občas setkáme při práci s poli.

ProměnnáVýznamImplicitně
$,řetězec, který bude tištěn mezi jednotlivými prvky seznamu (ne v řetězci)""
$"řetězec, který bude tištěn mezi jednotlivými prvky seznamu uvedeném v řetězci označeným uvozovkami." "
$\řetězec, který bude vytištěn na konci seznamu""

Sledujte, jak se změní poslední příklad použitím proměnných $, a $\:

$, = ", ";
$" = "-"; #v tomto případě neovlivní výsledek
$\ = "\n";

print "a", "b", "c";

Vytiskne se řetězec "a, b, c\n". Mezi jednotlivými prvky pole je vždy čárka a mezera a za poslední položkou je znak nového řádku.

$" působí na tisknuté pole, vložené do řetězce:

$, = ", "; #v tomto případě neovlivní výsledek
$" = "-"; #zkuste zakomentovat tento řádek, implicitním obsahem je mezera

@p = ("a", "b", "c");
print "@p";

Vytiskne se řetězec "a-b-c". Zkusme nyní pro zvýraznění rozdílu odstranit z příkazu print uvozovky. Pole by potom nebylo v řetězci, místo $" by se mezi jednotlivé prvky tiskla hodnota v proměnné $, a výsledkem by bylo "a, b, c".

Poslední prvek pole

Zápis $#pole vrací poslední (nejvyšší) index pole. Je ekvivalentní zápisu $pole[-1].

Operátor rozsahu

Přiřaďme nyní do prvků s indexy 5-14 pole @pole2 hodnoty prvků s indexy 2-11 pole @pole:

@pole2[5 .. 14] = @pole[2 .. 11];

Výraz 5 .. 14 má stejný význam jako 5, 6, 7, 8, 9, 10, 11, 12, 13, 14. Při použití na místě indexu pole jde o tzv. řez polem.

Na základě této informace vytvořme pole s hodnotami 1 až 100:

@pole = (1 .. 100);

Operátor rozsahu funguje i na malá a velká písmena anglické abecedy:

@pole = ("c" .. "z"); # výsledkem je pole obsahující znaky c až z

Podívejme se na několik dalších příkladů.

$, = ", ";
$\ = "\n";

print 1.88 .. 2.21;                        #stejné jako 1 .. 2. Desetinná část se odřízne
print "001" .. "010";                      #001, 002, 003 ... 010\n
print "abc" .. "abx";                      #abc, abd, abe ... abx\n
print "bc" .. "de";                        #bc, bd, be ... bz, ca, cb ... dd, de\n
print 0 .. 9, "A" .. "F";                  #nic nového, pouze se tiskne seznam složený ze dvou seznamů
print 0 .. 3, "a" .. "c", $promenna, "XXX";#prakticky totéž
print -10 .. 10;                           #záporná čísla nejsou překážkou
print 10 .. 1;                             #ale pozpátku neumí

Setřídění pole - funkce sort

Pro řazení polí má Perl speciální vestavěnou funkci. Funkce sort řadí (implicitně) podle ASCII tabulky. Další funkce - reverse - obrací pořadí prvků pole. Poslední prvek prohodí s prvním, předposlední s druhým atd.

@p = qw(a g d f e b c); #spřeházený seznam
print @p;               #agdfebc
@p = sort @p;           #setřídí podle abecedy
print @p;               #abcdefg
@p = reverse @p;        #obrátí pořadí prvků
print @p;               #gfedcba

Tento systém má háček. Porovnává po znaku, zleva doprava, takže 6 je větší než 44. Na čísla je předchozí zápis nepoužitelný.

Řazení čísel ale zvládne sort také. Slouží k tomu speciální konstrukce {$a <=> $b}. Porovnává vždy dva prvky pole, určuje, který je větší a takto je řadí. Proměnné $a a $b jsou dvě speciální proměnné určené pro tento účel. Když prohodíme jejich pořadí, setřídí se pole sestupně.

@p = (6, 1, 3, 5, 4, 2);  #přeházený seznam
print @p;                 #613542
@p2 = sort {$a <=> $b} @p;#setřídí podle velikosti vzestupně
print @p2;                #123456
@p3 = sort {$b <=> $a} @p;#setřídí podle velikosti sestupně
print @p3;                #654321

Poznamenejme, že sort bez uvedeného bloku vlastně znamená totéž, co bychom získali uvedením {$a cmp $b}. Do bloku lze napsat v podstatě cokoliv, co vrací 0, 1 nebo -1 na základě speciálních proměnných. Například pro setřídění názvů souborů podle času jejich vzniku bychom mohli napsat toto:

@files = sort {(stat($a))[9] <=> (stat($b))[9]} @files;

Nastavení jazyka

Dalším problémem pro třídění podle abecedy je čeština. Implicitně Perl třídí písmena s háčky až na konec a písmeno ch za c. To se dá vyřešit pomocí locales. V systému nastavme hodnotu proměnné LANG na czech:

$ export LANG=czech

Je dobré si tento řádek připsat do nějakého konfiguračního souboru, který se spouští při zapnutí systému.

Dalším krokem je přidat do samotného programu příkaz use locale.

#!/usr/bin/perl
use locale;

$, = ", ";
$\ = "\n";

@p = qw(a d f č ď e c ch u); #přeházený seznam
print sort @p;

Pokud máme správně nastavenou proměnnou prostředí LANG, měli bychom spatřit výstup:

$ ./sort.pl
a, c, č, d, ď, e, f, ch, u
$

Teď smažme řádek use locale;:

$ ./sort.pl
a, c, ch, d, e, f, u, č, š
$

Třídění pole - funkce grep

grep je analogií stejnojmenného unixového příkazu. Vyhledává prvky v seznamu, ve kterých byl nalezen určitý podřetězec a ty vrací jako seznam. Existují dvě možnosti zápisu grepu:

@vyhovujici_prvky = grep {test} seznam
@vyhovujici_prvky = grep /regulární_výraz/, seznam

V prvním řádku grep postupně přiřazuje každý prvek seznamu do výchozí proměnné $_, ověří, zda vyhovuje výrazu ve složených závorkách, a pokud ano, prvek se ocitne i ve výsledném poli.

$, = ", ";
@pole = (8, 2, 4, 6, 9, 5);
print grep {$_ < 7} @pole; #vytiskne 2, 4, 6, 5 - tedy ty prvky, které jsou menší než 7

V druhém zápisu je namísto testovacího výrazu použit vzor. To je záležitost regulárních výrazů. grep vrací seznam prvků z původního pole, které danému vzoru vyhovují

$, = ", ";
@pole = ("chomp", "print", "grep", "sin", "index");
print grep /^.+in/, @pole;

Vypsáno bude "print, sin" - tedy řetězce, které obsahují podřetězec in, ale nezačínají jím. Regulární výrazy budeme již brzy řešit v samostatných dílech.

Příklad - výčet potřebných platidel

Podívejme se opět na trochu delší ukázku programu. Zadání úlohy je následující: Napište program, který načte ze vstupu číslo, vyjadřující peněžitý obnos. Definujte pole, ve kterém budete mít seznam hodnot českých platidel (haléřové mince neuvažujeme - tedy v poli budou prvky pro 1 Kč, 2 Kč, 5 Kč, 10 Kč ... 2000 Kč, 5000 Kč). Obnos poté rozdělte na hodnoty jednotlivých platidel tak, aby byl rozložen do co nejvyšších platidel. Přitom se snažte o obecnost - tedy například přibude-li platidlo, musí stačit jeho hodnotu přiřadit do pole platidel. Ať pracuje program. Výsledky (kolik kterých platidel) tiskněte na výstup.

Řešení

Začátek je jasný, definujeme pole platidel:

#!/usr/bin/perl

@platidla = (1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000);

Načteme částku. Tady se na chvilku zastavíme. Musíme ošetřit hned tři nestandardní případy. Uživatel vůbec nemusí zadat číslo nebo může být záporné. Oba problémy vyřešíme tak, že otestujeme, zda je částka větší než 0 (poznámka: pokud tedy uživatel zadá "506xyz", bude částka zkonvertována na 506). Dalším problémem jsou desetinná čísla. Je zadáno, že haléře máme zanedbat, takže funkcí int zadanou haléřovou částku ořízneme.

print "Zadej částku v Kč: ";
$castka = int <STDIN>;

if ($castka < 1){
    die "Toto není regulérní častka.\n";
}

Zamysleme se nad tím, jak budeme postupovat dál. Určitě budeme mít nějaký cyklus. V každé jeho iteraci budeme odečítat příslušný počet jednoho druhu platidla. Protože mají být platidla maximálně vysoké, bude výhodné v první iteraci odečítat nejvyšší platidlo, v druhé iteraci druhé nejvyšší atd. Proto nejprve seřaďme pole sestupně:

@platidla = sort {$b <=> $a} @platidla;

Zbývá najít nějaký vhodný test, který by rozhodoval o pokračování cyklu. Iterovat se bude tolikkrát, kolik prvků má pole @platidla. Zaveďme ještě před cyklem proměnnou $pozice, kterou nastavíme na 0. Každou iterací se inkrementuje. Až nabude hodnoty stejné jako počet prvků pole @platidla, cyklus se zastaví. A protože předem známe počet iterací, použijeme cyklus for. Nic nám nebrání začít se samotným cyklem.

for my $pozice (0 .. @platidla){

    ... n krát odečtení hodnoty platidla ...

}

Teď je třeba se zamyslet, jak to bude vypadat v cyklu. Zjistíme počet kusů daného platidla, které lze maximálně odečíst, aby byl výsledek kladný. Stačí od zbývající částky odečíst zbytek, který by zbyl po dělení hodnotou aktuálního platidla - výsledkem bude nejvyšší částka dělitelná hodnotou platidla, ale stále nižší než zbývající částka. Tuto vzniklou částku vydělíme aktuální hodnotou platidla:

    $pocet_kusu = ($castka - ($castka % $platidla[$pozice])) / $platidla[$pozice];

Získali jsme počet kusů aktuálního platidla, který lze odečíst od zbývající částky. Tak to udělejme:

    $castka -=  $pocet_kusu * $platidla[$pozice];

Poznámka: Možná jste si všimli, že pokud bychom tyto dva řádky spojili do jednoho (za $pocet_kusu v druhém dosadili hodnotu prvního), získali bychom zlomek, který obsahuje v čitateli i jmenovateli hodnotu $platidla[$pozice], a tudíž jde zkrátit. $pocet_kusu ale ještě budeme potřebovat.

To je téměř celé. Zbývá zařídit nějaký výstup. Využijme cyklu a výsledky budeme vypisovat přímo v něm. Ještě před cyklus ale vložme pro pořádek velikost původního obnosu:

print "K vyplacení $castka Kč bude potřeba:\n";

A v cyklu bude:

    print "$pocet_kusu kusů platidla $platidla[$pozice].\n";

Jsme hotovi, nyní se pokochejme pohledem na chod právě vzniklého programu:

$ ./vycet.pl
Zadej částku v Kč: 19749
K vyplacení 19749 Kč bude potřeba:
3 kusů platidla 5000.
2 kusů platidla 2000.
0 kusů platidla 1000.
1 kusů platidla 500.
1 kusů platidla 200.
0 kusů platidla 100.
0 kusů platidla 50.
2 kusů platidla 20.
0 kusů platidla 10.
1 kusů platidla 5.
2 kusů platidla 2.
0 kusů platidla 1.
$

Zkusme do pole @platidla přidat ještě bankovku v hodnotě 10000. Mělo by to teoreticky také fungovat.

$ ./vycet.pl
Zadej částku v Kč: 19749
K vyplacení 19749 Kč bude potřeba:
1 kusů platidla 10000.
1 kusů platidla 5000.
2 kusů platidla 2000.
0 kusů platidla 1000.
1 kusů platidla 500.
1 kusů platidla 200.
0 kusů platidla 100.
0 kusů platidla 50.
2 kusů platidla 20.
0 kusů platidla 10.
1 kusů platidla 5.
2 kusů platidla 2.
0 kusů platidla 1.
$

Možná vám přijde, že není jednoduché se ve výstupu zorientovat okamžitě. Je tam moc řádků. Ještě program trochu pozměníme. Není-li od nějakého platidla ani kus, nebudeme řádek výbec vypisovat. Použijme konstrukci podmínka za příkazem. Vykonání příkazu print v cyklu podmíníme nenulovým počtem kusů daného platidla:

    print "$pocet_kusu kusů platidla $platidla[$pozice].\n" if $pocet_kusu;

Ještě odstraníme platidlo 10000 a program opět vyzkoušíme:

$ ./vycet.pl
Zadej částku v Kč: 19749
K vyplacení 19749 Kč bude potřeba:
3 kusů platidla 5000.
2 kusů platidla 2000.
1 kusů platidla 500.
1 kusů platidla 200.
2 kusů platidla 20.
1 kusů platidla 5.
2 kusů platidla 2.
$

Je to hned o něco lepší. Celý zdrojový kód je možné stáhnout. K dokonalosti chybí přidat deklarace proměnných a zformátovat výstup. Formátování časem budeme věnovat celý díl.

Jako bonus si můžete zkusit program upravit tak, aby správně česky skloňoval slovo kus.

Verze pro tisk

pridej.cz

 

DISKUZE

Hezké, hezké 12.8.2005 16:11 Hynek (Pichi) Vychodil
  L Re: Hezké, hezké 12.8.2005 16:14 Ivan Majer
    L Re: Hezké, hezké 12.8.2005 18:01 Hynek (Pichi) Vychodil
      |- Re: Hezké, hezké 12.8.2005 18:22 Hynek (Pichi) Vychodil
      L Re: Hezké, hezké 12.8.2005 18:48 Aleš Hakl




Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

14.11.2017 16:56 /František Kučera

Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tradičně první čtvrtek před třetím pátkem v měsíci: 16. listopadu od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).


Přidat komentář

12.11.2017 11:06 /Redakce Linuxsoft.cz
PR: 4. ročník odborné IT konference na téma Datová centra pro business proběhne již ve čtvrtek 23. listopadu 2017 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00. Konference o návrhu, budování, správě a efektivním využívání datových center nabídne odpovědi na aktuální a často řešené otázky, např Jaké jsou aktuální trendy v oblasti datových center a jak je využít pro vlastní prospěch? Jak zajistit pro firmu či jinou organizaci odpovídající služby datových center? Podle jakých kritérií vybrat dodavatele služeb? Jak volit součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně spravovat datové centrum? Jak eliminovat možná rizika? apod.
Přidat komentář

13.9.2017 8:00 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tentokrát netradičně v pondělí: 18. září od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

3.9.2017 20:45 /Redakce Linuxsoft.cz
PR: Dne 21. září 2017 proběhne v Praze konference "Mobilní řešení pro business". Hlavní tématy konference budou: nejnovější trendy v oblasti mobilních řešení pro firmy, efektivní využití mobilních zařízení, bezpečnostní rizika a řešení pro jejich omezení, správa mobilních zařízení ve firmách a další.
Přidat komentář

15.5.2017 23:50 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt, který se bude konat ve čtvrtek 18. května od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.5.2017 16:42 /Honza Javorek
PyCon CZ, česká konference o programovacím jazyce Python, se po dvou úspěšných ročnících v Brně bude letos konat v Praze, a to 8. až 10. června. Na konferenci letos zavítá např. i Armin Ronacher, známý především jako autor frameworku Flask, šablon Jinja2/Twig, a dalších projektů. Těšit se můžete na přednášky o datové analytice, tvorbě webu, testování, tvorbě API, učení a mentorování programování, přednášky o rozvoji komunity, o použití Pythonu ve vědě nebo k ovládání nejrůznějších zařízení (MicroPython). Na vlastní prsty si můžete na workshopech vyzkoušet postavit Pythonem ovládaného robota, naučit se učit šestileté děti programovat, efektivně testovat nebo si v Pythonu pohrát s kartografickým materiálem. Kupujte lístky, dokud jsou.
Přidat komentář

2.5.2017 9:20 /Eva Rázgová
Putovní konference československé Drupal komunity "DrupalCamp Československo" se tentokrát koná 27. 5.2017 na VUT FIT v Brně. Můžete načerpat a vyměnit si zkušenosti z oblasti Drupalu 7 a 8, UX, SEO, managementu týmového vývoje, využití Dockeru pro Drupal a dalších. Vítáni jsou nováčci i experti. Akci pořádají Slovenská Drupal Asociácia a česká Asociace pro Drupal. Registrace na webu .
Přidat komentář

1.5.2017 20:31 /Pavel `Goldenfish' Kysilka
PR: 25.5.2017 proběhne v Praze konference na téma Firemní informační systémy. Hlavními tématy jsou: Informační systémy s vlastní inteligencí, efektivní práce s dokumenty, mobilní přístup k datům nebo využívání cloudu.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

18.9.2017 14:37 / Rojas
high security vault

15.9.2017 7:33 / Wilson
new zealand childcare jobs

31.8.2017 12:11 / Jaromir Obr
Re: ukůládání dat ze souboru

30.7.2017 11:12 / Jaromir Obr
Národní znaky

27.7.2017 12:24 / Jaromir Obr
Cteni/zapis

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2017) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze