Programowanie 2
Perl - Tablice asocjacyjne oraz funkcje tablicowe
Marcin Junczys-Dowmunt
junczys@amu.edu.pl
Zakład Logiki Stosowanej
http://www.logic.amu.edu.pl
8. grudnia 2009
Marcin Junczys-Dowmunt
1/16
Dzisiejszy wykład
I
Skupimy się na jednym z najpotężniejszych narzędzi w Perlu –
na tzw. haszach:
I
omówimy istotę haszów
I
sposoby inicjalizacji haszów
I
sposoby dodawania i usuwanie elementów
I
sposoby przeglądania haszów
I
Omówimy powiązania między haszami i tablicami
I
w tym sortowanie elementów tablic i haszów za pomocą sort
I
tworzenie np. haszów z tablic za pomocą map
Marcin Junczys-Dowmunt
2/16
Co to jest hasz?
I
Hasz jest strukturą podobną do tablicy, ale zamiast indeksów
liczbowych hasz używa kluczy
I
Bardziej skomplikowana nazwa dla haszy to tablice
asocjacyjne, ponieważ kojarzą ze sobą klucze i wartości
I
Kluczem hasza może być dowolna wartość skalarna, czyli
liczba, łańcuch znakowy
I
Przedrostkiem dla hasza jest znak %, który ma przypominać
parę elementów skojarzonych
I
Zamiast nawiasów [] korzystamy z {} przy odwoływaniu się do
wartości haszów
I
Ponoć by programować w Perlu, trzeba myśleć w haszach
Marcin Junczys-Dowmunt
3/16
Inicjalizacja haszów
1
my
% d z w i e k i = (
lew = >
" g r r r "
,
p i e s = >
" hau "
,
kot = >
" m i a u "
,
5
" t y g r y s b e n g a l s k i "
= >
" r o a r "
);
p r i n t
" Lew r o b i "
. $ d z w i e k i {
" lew "
} .
" \ n "
;
p r i n t
" P i e s r o b i "
. $ d z w i e k i { p i e s } .
" \ n "
;
10
p r i n t
" Kot r o b i $ d z w i e k i { kot }\ n "
;
p r i n t
" T y g r y s r o b i $ d z w i e k i { ’ t y g r y s b e n g a l s k i ’}\ n "
;
I
Kanoniczny sposób inicjalizacji haszów
I
Kojarzymy ze sobą nazwy zwierząt oraz wydawane dźwięki
I
Gdy klucz składa się z samych znaków alfanumerycznych,
możemy opuścić cudzysłów
Marcin Junczys-Dowmunt
4/16
Dodawanie elementów do haszów
1
my
% o c z y ;
$ o c z y { kot } =
" 2 "
;
@ o c z y { mrowka , w a z k a } = (4 ,
" m i l i o n "
);
5
my
@ s t w o r y =
qw
( p a n t o f e l e k , c z l o w i e k , pajak , m u c h a );
@ o c z y { @ s t w o r y } = (0 , 2 , 8 ,
" d u z o za d u z o "
);
I
Możemy dodawać dowolną liczbą elementów do hasza
I
Ponieważ nie ma określonej kolejności elementów w haszu, nie
potrzebujemy funkcji typu unshift, push
I
Podobnie jak dla tablic istnieją wycinki haszów
I
Każdy wycinek z hasza jest tablicą(!), stąd przedrostek @
Marcin Junczys-Dowmunt
5/16
Przeglądanie haszów - według kluczy
1
my
% s ł o w n i k = (
s z a f a = >
" r z e c z o w n i k "
, w i e l k i = >
" p r z y m i o t n i k "
,
m r u g a c = >
" c z a s o w n i k "
, k r o t k o = >
" p r z y s l o w e k "
,
);
5
f o r e a c h my
$ s l o w o _ k l u c z (
k e y s
% s ł o w n i k ) {
p r i n t
" W y r a z $ s l o w o _ k l u c z to "
.
" $ s ł o w n i k { $ s l o w o _ k l u c z }\ n "
;
}
I
Funkcja wbudowana keys zwraca listę wszystkich kluczy
danego hasza
I
Można tę listę zapisać do zmiennej tablicowej lub użyć w
dowolnym kontekście listowym
I
Elementy w haszach nie są uporządkowane, więc lista
zwrócona przez keys też nie jest
Marcin Junczys-Dowmunt
6/16
Przeglądanie haszów - według wartości
1
my
% s ł o w n i k = (
s z a f a = >
" r z e c z o w n i k "
, w i e l k i = >
" p r z y m i o t n i k "
,
m r u g a c = >
" c z a s o w n i k "
, k r o t k o = >
" p r z y s l o w e k "
,
);
5
f o r e a c h my
$ c z e s c _ m o w y _ w a r t o s c (
v a l u e s
% s ł o w n i k ) {
p r i n t
" S l o w n i k z a w i e r a n a s t . c z e s c i m o w y : "
.
" $ c z e s c _ m o w y _ w a r t o s c \ n "
;
}
I
Funkcja wbudowana values zwraca listę wszystkich wartości
danego hasza (odpowiednik keys)
I
W przeciwieństwie do kluczy, wartości w haszu mogą się
powtarzać (hasze są lewostronnie jednoznaczne)
I
Nie ma bezpośredniej możliwości wyświetlenia klucza do
odpowiedniej wartości
Marcin Junczys-Dowmunt
7/16
Przeglądanie haszów - według par kluczy i wartości
1
my
% s ł o w n i k = (
s z a f a = >
" r z e c z o w n i k "
, w i e l k i = >
" p r z y m i o t n i k "
,
m r u g a c = >
" c z a s o w n i k "
, k r o t k o = >
" p r z y s l o w e k "
,
);
5
w h i l e
(
my
( $wyraz , $ c z e s c _ m o w y ) =
e a c h
% s ł o w n i k ) {
p r i n t
" W y r a z $ w y r a z to $ c z e s c _ m o w y \ n "
;
}
I
Funkcja each w kontekście listowym dla podanego hasza
zwraca parę klucz-wartość (czyli listę dwuelementową)
I
Przy każdym wywołaniu each zwraca kolejną parę, aż
zabraknie par w haszu
I
W kontekście skalarnym zwraca jedynie kolejne klucze
Marcin Junczys-Dowmunt
8/16
Usuwanie elementów z hasza
1
my
% h a s z = ( k l u c z 1 = > 1 , k l u c z 2 = > 2 , k l u c z 3 = > 3);
# my % h a s z = (" k l u c z " , 1 , " k l u c z 2 " , 2 , " k l u c z 3 " , 3);
my
$ s k a l a r 1 =
d e l e t e
$ h a s z { k l u c z 1 };
5
# $ s k a l a r 1 r ó w n y 1
my
$ s k a l a r 2 =
d e l e t e
@ h a s h {
qw
( k l u c z 1 k l u c z 2 )};
# s k a l a r 2 r ó w n y 2
10
@ t a b l i c a =
d e l e t e
@ h a s h {
qw
( k l u c z 1 k l u c z 2 k l u c z 3 )};
# @ t a b l i c a r ó w n a ( undef , undef , 3)
I
Funkcja wbudowana delete usuwa podane elementy z hasza
I
W kontekście skalarnym zwraca ostatni usunięty element lub
undef jeśli element nie istnieje
I
W kontekście listowym zwraca listę wszystkich usuniętych
elementów, w tym undef, jeśli jakiś z elementów nie istnieje
Marcin Junczys-Dowmunt
9/16
Sprawdzanie czy element istnieje w haszu
1
my
% m i t y = (
y e t i = > 0 ,
g w i a z d o r = >
" "
,
w i l k o l a k = >
u n d e f
5
);
c h o m p
(
my
$ t e s t = < STDIN >);
if
(
e x i s t s
( $ m i t y { $ t e s t })) {
p r i n t
" $ t e s t i s t n i e j e , w a r t o ś ć ’ $ m i t y { $ t e s t } ’\ n "
;
10
}
e l s e
{
p r i n t
" $ t e s t nie i s t n i e j e \ n "
;
}
I
Funkcja exists sprawdza, czy dany element istnieje w haszu
I
Istnienie takiej funkcji jest konieczne, ponieważ wartość
skojarzona z danym kluczem może być logicznym fałszem
Marcin Junczys-Dowmunt
10/16
Hasze - podsumowanie
I
Hasze to struktury podobne do list, które mają zawsze
parzystą liczbę elementów (możemy zapisywać tablice do
haszów i hasze do tablic)
I
Hasze można traktować jak zbiory par klucz-wartość (zbiór
par uporządkowanych, relacja)
I
Elementy haszów nie są uporządkowane (tak jak zbiór)
I
Klucze haszy nie mogą się powtarzać, próba dodania pary
klucz-wartość dla istniejące klucza spowodują nadpisanie
poprzedniej wartości (relacja lewostronnie jednoznaczna)
I
Operator => jest synonimem przecinka , (operator listowy),
dodatkowo wymusza po lewej stronie kontekst łańcuchowy
(nawet gdy klucz jest liczbą!)
Marcin Junczys-Dowmunt
11/16
Sortowanie tablic
Jeśli chcemy uporządkować tablicę według jakiegoś określonego
porządku korzystamy z funkcji sort
1
@ l i s t a _ o b e c n o s c i =
qw
( Z e n o n W l a d e k A n t e k M i r e k E d e k );
p r i n t j o i n
(
" \ n "
,
s o r t
@ l i s t a _ o b e c n o s c i ).
" \ n "
;
Sortowanie działa też na liczbach
1
@ l i c z b y = (4 ,7 ,13 ,9 ,5 ,2 ,10 ,7);
p r i n t j o i n
(
" , "
,
s o r t
@ l i c z b y ).
" \ n "
;
Ale może działać dziwnie dla wartości mieszanych
1
@ m i e s z a n e = (4 ,
" A n t e k "
,13 ,9 ,
" Z e n o n "
,2 ,10 ,
" M i r e k "
);
p r i n t j o i n
(
" , "
,
s o r t
@ m i e s z a n e ).
" \ n "
;
Według jakiego porządku została posortowana ostatnia lista?
Marcin Junczys-Dowmunt
12/16
Sortowanie tablic - ciąg dalszy
Funkcja sort może działać według dowolnych porządków
1
@ m i e s z a n e = (4 ,
" A n t e k "
,13 ,9 ,
" Z e n o n "
,2 ,10 ,
" M i r e k "
);
p r i n t j o i n
(
" , "
,
s o r t
{
$a <= > $b or $a cmp $b
} @ m i e s z a n e ).
" \ n "
;
Kto potrafi wytłumaczyć, dlaczego taki zapis porządkuje w
obserwowany sposób?
Wskazówka: Operator logiczny or nie sprawdza wyrażenia po jego
prawej stronie, gdy wyrażenie po lewej stronie jest prawdziwe
I
Zmienne $a i $b reprezentują dwie porównywane wartości
sortowanej listy
I
Określając sposoby porównywania, określamy porządki
sortowania
I
Sortowanie, gdzie liczby poprzedzają łańcuchy jest trudniejsze
Marcin Junczys-Dowmunt
13/16
Inne sortowania
1
@ l i s t a =
qw
( W a l d e k Z e n e k t o r t T o m e k O l g a Ala w o r e k );
p r i n t j o i n
(
" , "
,
s o r t
@ l i s t a ).
" \ n "
;
p r i n t j o i n
(
" , "
,
s o r t
{
lc
( $a ) cmp
lc
( $b )} @ l i s t a ).
" \ n "
;
5
p r i n t j o i n
(
" , "
,
s o r t
{
lc
( $b ) cmp
lc
( $a )} @ l i s t a ).
" \ n "
;
@ r e v l i s t a =
r e v e r s e s o r t
{
lc
( $a ) cmp
lc
( $b )} @ l i s t a ;
p r i n t j o i n
(
" , "
, @ r e v l i s t a ).
" \ n "
;
10
p r i n t j o i n
(
" , "
,
s o r t
{
l e n g t h
( $a ) <= >
l e n g t h
( $b ) or $a cmp $b
} @ l i s t a ).
" \ n "
;
p r i n t j o i n
(
" , "
,
s o r t
{
15
r e v e r s e
( $a ) cmp
r e v e r s e
( $b )
} @ l i s t a ).
" \ n "
;
Marcin Junczys-Dowmunt
14/16
Sortowanie a hasze
1
my
% h a s z = (
" Ala "
= >
" i m i e "
,
" p i e s "
= >
" z w i e r z e "
,
" L o n d y n "
= >
" m i a s t o "
,
" a r b u z "
= >
" o w o c "
,
);
5
f o r e a c h
$ k l u c z (
s o r t
{
lc
( $a ) cmp
lc
( $b ) }
k e y s
% h a s z ){
p r i n t
" $ k l u c z = > $ h a s z { $ k l u c z }\ n "
}
f o r e a c h
$ k l u c z (
10
s o r t
{ $ h a s z { $a } cmp $ h a s z { $b }}
k e y s
% h a s z ){
p r i n t
" $ k l u c z = > $ h a s z { $ k l u c z }\ n "
}
I
Za pomocą funkcji sort oraz np. keys możemy sobie sami
określić porządek w haszu
I
W bloku określającym porządek można również korzystać z
przetwarzanej tablicy haszującej (np. pobierając wartość dla
danego klucza)
Marcin Junczys-Dowmunt
15/16
Podsumowanie
I
Hasze to jeden z najważniejszych mechanizmów w Perlu
I
Bardzo wiele zadań programistycznych można rozwiązać w
elegancki sposób za pomocą haszy (zadania domowe)
I
Poznaliśmy kilka funkcyjnych sposobów przetwarzania list i
haszów (wejściem do funkcji jest lista, wyjściem lista
przetworzona)
I
Między innymi dzięki tym funkcjom składnia Perla jest taka
zwięzła
I
Zadania wykonywane przez te funkcje zajęłyby kilka wierszy
każdym tradycyjnym języku programowania
Marcin Junczys-Dowmunt
16/16