Steven Holzner Czarna Księga - Perl Wprowadzenie Niniejsza książka ma dać Ci wszystko, czego potrzebujesz, aby stać się programistą w Perlu, a to przecież znaczy bardzo wiele. Perl nie jest zwykłym językiem programowania. Osoby go używające oddają się pracy z pasją, są zdolni do wzruszenia i zadziwienia, ale także do rozpaczy i frustracji. Perl to więcej niż język programowania — to praprzyczyna, zajęcie dla poetów i fanatyków komputera. Perl może być złożony i trudny do opanowania, ale dla prawdziwego entuzjasty nie ma innej drogi. Zresztą po przeczytaniu tej książki zrozumiesz, o co mi chodzi. W ciągu dwunastu lat tryumfalnego pochodu przez informatyczne salony praktyczny język pobierania danych i raportowania (przez niektórych często nazywany „chorobliwie zabałaganionym generatorem śmieci") stał się przyczyną wielu emocji. Wiele osób poświęca zadziwiająco dużo czasu na jego używanie, udoskonalanie i rozbudowę. Autor swoją pracę z Perlem zaczął wiele lat wcześniej, zanim wpadł mu do głowy pomysł napisania tej książki. Być może sposób myślenia — zaprezentowany w tej książce — spodoba się niektórym Czytelnikom, powiększając grono prawdziwych entuzjastów Perla? Zawartość tej książki W tej książce nie tylko podano kompletny opis składni języka Perl, ale opisano także sam język oraz zakres jego używania. Na przykład Perla można znaleźć niemalże w każdym miejscu w Internecie, zatem w książce podano wiele praktycznych przykładów CGI. Innym popularnym zestawieniem jest połączenie Perla i Tcl/Tk, co umożliwia między innymi wyświetlanie okien, przycisków czy menu — również ten temat jest opisany w książce. Istnieje jeszcze kilka innych zagadnień, omówionych tutaj, a związanych z Perlem, takich jak łączenie tego języka z bazami danych, serwerami OLE Windows, innymi procesami i tak dalej. Jak widać, proponowana książka ma dać obraz tego, jak język Perl jest używany dzisiaj. W Perlu 5 istnieje wiele rozszerzeń, które zostaną w tej książce przedstawione. Czytelnik znajdzie w niej m.in. następujące zagadnienia: pisanie czytelnego kodu Perl, użycie bloków BEGIN i END w pakietach, zgodność z POSIX, programowanie obiektowe, dowolne zagnieżdżanie struktur danych, zakresy leksykalne, użycie modułów i tak dalej. Język Perl 5 stanowi całkiem nową jakość i na tej właśnie wersji niniejsza książka jest oparta. Książkę tę podzielono na osobne, wygodne w użyciu tematy, a jest ich blisko pięćset. Każdy temat zawiera omówienie jednego zagadnienia programistycznego. Poniżej znajduje się lista wybranych tematów: * składnia Perla 5: instrukcje i deklaracje, * interaktywne uruchamianie skryptów Perla, * wprowadzanie i wyprowadzanie tekstu, * tworzenie zmiennych skalarnych, * kontekst skalarny i listowy, * tworzenie tablic i asocjacji, * typy globalne i tabele symboli, * operatory Perla, * wzorce tekstowe i obsługa tekstów, * tworzenie procedur, * zmienne o ograniczonym zakresie, * zmienne tymczasowe, * zmienne statyczne, * procedury rekurencyjne, * anonimowe tablice, asocjacje i procedury, * wskaźniki Perla, * wskaźniki symboliczne, * szablony funkcji, * zmienne specjalne Perla, * funkcje wbudowane Perla, * funkcje POSIX, * formaty Perla, * komunikacja międzyprocesowa, * OLE Win32, * obsługa plików, * pliki baz danych DBM, * blokowanie plików, * operatory do obsługi katalogów, * testy wydajności, * operacje zależne od ustawień narodowych, * wydzielanie kodu do zabezpieczonych części, * Perl/Tk: użycie pakietu Tk, * rekordy złożone, * tablice tablic, tablice asocjacyjne tablic asocjacyjnych, tablice tablic asocja cyjnych i tablice asocjacyjne tablic, * listy powiązane i bufory pierścieniowe, * pakiety Perla, * konstruktor/ i destruktory pakietów, * rozbijanie pakietów między pliki, * moduły Perla, * eksportowanie symboli z modułów, * automatyczne ładowanie procedur modułu, * klasy Perla, * metody klas, * zmienne instancji i klas, * dziedziczenie klas, * dziedziczenie wielokrotne, * wiązanie skalarów, tablic i asocjacji, * przechwytywanie błędów wykonania, * debugger Perla, * PerlScript, * programowanie CGI, * tworzenie i używanie w CGI kontrolek HTML, * CGI a bezpieczeństwo, * oznaczanie danych jako skażonych, * przyznawanie skryptom CGI dodatkowych uprawnień, * tworzenie liczników odwiedzin, * tworzenie książki gości, * wysyłanie listów elektronicznych ze skryptów CGI, * tworzenie chat, * zagadnienia bezpieczeństwa w przypadku wielu użytkowników, * odpieranie ataków DOS, * odświeżanie elementów graficznych HTML, * tworzenie cookies, * przechowywanie w stronach danych pomiędzy kolejnymi wywołaniami skryptu. W tej książce używamy kilka konwencji, o których należy pamiętać. Kiedy konieczne jest wskazanie jakiegoś wiersza kodu, będzie on zaznaczony pogrubieniem: $text = "Hello!\n" print $text; Kiedy natomiast należy pokazać wyniki działania skryptu — niezależnie od samego skryptu, wyniki te będą zapisywane kursywą: Stext = "Hello!\n" print Stext; Hello Co będzie nam potrzebne? Autor opiera się w książce na języku Perl w wersji 5.005. Perl jest darmowy, wystarczy go załadować i zainstalować — zagadnienie to zostanie omówione w rozdziale pierwszym. W przypadku korzystania z systemu wieloużytkownikowego Perl może już być zainstalowany; aby to sprawdzić, wystarczy w wierszu poleceń wyświetlić numer wersji Perła: perl -v Warto wziąć pod uwagę jeszcze dwie sprawy, jeśli chodzi o samodzielne uruchamianie Perla: zaleca się użycie przełącznika -w, który powoduje wyświetlanie ostrzeżeń (być może kiedyś wyświetlanie ostrzeżeń stanie się w Perlu standardem), oraz użycie dyrektywy kompilatora use strict, która powoduje, że konieczne jest deklarowanie wszystkich zmiennych i innych symboli. Zadbanie o te dwie rzeczy zaoszczędzi wielu kłopotów przy uruchamianiu programów. Trzeba będzie jeszcze w jakiś sposób tworzyć same skrypty Perla. Wspomniane skrypty to zwykłe pliki tekstowe, zawierające instrukcje i deklaracje Perla. Aby utworzyć skrypt Perla, potrzebny jest edytor tekstowy, który potrafi pliki zapisywać jako zwykły tekst. Więcej informacji na ten temat znajdziesz w rozdziale L, w punkcie „Zapisywanie skryptów Perla". Istnieje jeszcze jedna dziedzina wiedzy, która jest nam zbędna — system operacyjny, w którym Perl działa. Wprawdzie w wielu książkach o Perlu niejako z góry zakłada się, że jesteś doświadczonym programistą Uniksa, ale tyrh razem będzie inaczej. Per! już nie jest ściśle związany z Uniksem i czas, aby zerwać z dawną tradycją książek o tym języku. Inne zasoby Istnieje mnóstwo informacji o Perlu, które mogą pomóc w nauce, zresztą wraz z Pędem jest dostarczana bogata dokumentacja. W systemach takich jak Windows dokumentacja ta ma postać zestawu powiązanych ze sobą stron HTML. W systemach wieloużytkonikowych dokumentacja zwykle jest dostępna- dzięki poleceniom systemu operacyjnego, obsługującym pomoc —jak np. mań w Uniksie. Funkcjonuje też mnóstwo grup dyskusyjnych dla programistów Perla: * comp.lang.perl.announce — grupa, na której stale coś się dzieje, * com.lang.perl.mixc — dynamiczna grupa, która między innymi rozsyła FAQ Perla (Często zadawane pytania), * com.lang.perl.modules — wszystko o tworzeniu modułów i wielokrotnym użyciu kodu, zarówno własnego, jak i cudzego, * comp.lang.perl.tk — informacje o łączeniu Perla z pakietem Tk języka Tcl. Pakiet Tk obsługuje szereg elementów graficznych, jak choćby przyciski czy menu, dzięki czemu elementy te można automatycznie używać także w Perlu. Obecnie pakiet Tk stał się bardzo popularny. Jeśli kogoś interesuje programowanie CGI, warto skorzystać z adresu grupy: * comp.infosystemx.www.authoring.cgi — w nazwie nie ma tu Perla, ale jest to dobre miejsce do rozmowy na temat zastosowania Perla w CGI. Istnieje także wiele stron o Perlu (zapytanie o Perla zwróciło l 527 903 stron): * strona główna Perla to www.perl.com, gdzie znajdziesz kod źródłowy i wersje Perla na różne systemy operacyjne, dokumentację, moduły, informacje o błędach i listę często zadawanych pytań (FAQ, dostępne pod adresem www:perl.com/perl/faq); * w witrynie CPAN (Comprehensive Perl Archive Network), dostępnej pod adresem www.cpan.org lub www.perl.com/CPAN-local/CPAN.html, znajdziesz samego Perla, jego moduły, rozszerzenia oraz mnóstwo innych rzeczy związanych z tym językiem. Jest to ogromne źródło wiedzy i zasobów dla wszystkich, którzy używają Perla. Przeglądając zawartość CPAN, znajdziesz mnóstwo przydatnego kodu — od rozszerzeń Perla umożliwiających obsługę obrazków po moduły internetowe do obsługi interfejsów do baz danych; * Instytut Perla spod adresu www.perl.com jest niedochodową organizacją, która — zgodnie ze przyjętymi przez siebie założeniami — „troszczy się o to, aby Perl był dostępny, przydatny i darmowy dla wszystkich zainteresowanych". Instytut ten jest ostoją perłowej społeczności i zapewnia fanom tego języka stałe kontakty; * strona języka Perl znajduje się pod adresem www.perl.com/perl/, a znajdziesz tam omówienie Perla, nowinki, listę dostępnych zasobów oraz oprogramowanie. Tam też są dostępne listy wysyłkowe na temat Perła; * istnieje wiele innych stron, które koncentrują się na pewnych aspektach pracy z Perlem — na bezpieczeństwie, programowaniu CGI i innych. Warto po prostu poszukać w Sieci, aby znaleźć jeszcze więcej ciekawych informacji. Można sięgnąć również po kwartalnik poświęcony Perłowi. Więcej informacji na jego temat znajdziesz pod adresem orwcmt.www.media.mit.edu/thej3erljournal. I to tyle informacji tytułem wstępu, teraz czas zająć poważniej się Perlem. Część I Składnia języka Perl Rozdział 1. Co o Perlu trzeba wiedzieć? W skrócie Zaczynamy od początku W tym rozdziale zaczniemy naukę Perla. Najpierw omówimy rzeczy najważniejsze — tworzenie i uruchamianie skryptów. Wiadomości z tego rozdziału to absolutne podstawy, których znajomość jest niezbędna w dalszej części książki. Omówimy tutaj też wyświetlanie tekstu i odczytywanie informacji podawanych przez użytkownika. Zapewne treści tutaj przedstawione dla większości czytelników nie będą niczym nowym, dlatego rozdział ten warto potraktować jako krótkie przypomnienie. Jednak niektóre informacje będą nowością— można przypuszczać na przykład, że bardzo niewiele osób zna wszystkie przełączniki wiersza poleceń Perla. Perl powstał w 1986 roku jako narzędzie do śledzenia przebiegu konfigurowania systemu w sieci, dziś zaś stał się przenośnym językiem używanym przez ogromną część użytkowników Sieci, dlatego Perla można spotkać niemalże wszędzie w Internecie. Jeśli znasz język angielski, mógłbyś zapytać, czemu „Perl", a nie „Pearl" (perła)! Okazuje się, że w czasie, kiedy Perl powstawał, istniał już język graficzny Pearl. Z drugiej strony jednak, jeśli z pełnej nazwy — Practical Extraction and Reporting Language — utworzyć akronim z użyciem wszystkich słów, to faktycznie otrzyma się nazwę Pearl. Perl jest językiem interpretowanym, który stworzono w celu analizowania plików tekstowych, pobierania z nich informacji i wyświetlania raportów tekstowych zawierających te informacje. Do uruchamiania skryptów Perla używa się interpretera Perla o nazwie perl (zwróć uwagę na różnicę w wielkości liter). Warto tu napomknąć, że istniej ą też kompilatory Perla. Niektórzy dziwią się popularności Perla, języka — bądź co bądź — związanego ze środowiskiem tekstowym, a do tego uruchamianym z wiersza poleceń, podczas gdy standardem stają się interfejsy graficzne, takie jak Windows. Wśród przyczyn sukcesu Perla i stale zwiększającej się jego popularności można wymienić następujące kwestie: * Wiele systemów operacyjnych nadal bazuje głównie na trybie tekstowym. * Perl jest językiem programowania działającym na różnych platformach sprzętowych i w różnych systemach operacyjnych, zaś różnice między poszczególnymi systemami są niewielkie i dotyczą tylko takich szczegółów, jak na przykład liczba bajtów używanych do przechowywania długiej liczby całkowitej; * Dzięki wykorzystaniu pakietu Tk.pm Perl stał się narzędziem graficznym (zajmiemy się tym dokładniej w rozdziale 13.), gdyż można w nim używać typowych elementów graficznych za pośrednictwem pakietu Tk języka Tcl. Użycie modułu Tk.pm umożliwia wyświetlanie okienek zawierających przyciski, menu i inne elementy bezpośrednio z Perla. * Tym, co przyciągnęło do Perla największą liczbę ludzi, jest programowanie CGI, które umożliwia realizację w Sieci strategii klient-serwer. Przy tworzeniu stron sieciowych nie jest problemem używanie trybu tekstowego, gdyż same strony są zapisane zwykłym tekstem. Programowanie CGI w Perlu to naprawdę bardzo ważny mechanizm, wobec tego będzie to jeden z głównych tematów niniejszej książki. Tyle wiadomości wstępnych powinno nam wystarczyć — teraz czas już zacząć pracę ze skryptami. Zagadnienia: Uzyskanie i instalacja Perla Perl jest darmowy, więc wystarczy go ściągnąć z Sieci i zainstalować. W przypadku korzystania z systemu wielodostępnego należy sprawdzić, czy Perl nie został już zainstalowany — w tym celu trzeba użyć polecenia: %perl -v W całej tej książce symbol % oznacza znak zachęty systemu Unix. Znaku tego nie należy wpisywać. Jeśli Perl był zainstalowany i określono do niego ścieżkę, powyższe polecenie wyświetli jego wersję oraz wersje zainstalowanych poprawek (poprawki są wy dawane co jakiś czas — zawierają poprawki błędów). Zwróć uwagę, że w niektórych systemach zainstalowane mogą być stare wersje Perla, na przykład wersja 4. W takiej sytuacji wersja 5 Perla może być dostępna pod poleceniem perl5 (wypróbuj to polecenie, jeśli polecenie perl -v wskazało, że istnieje starsza wersja Perla): %perl5 -v Jeśli nie masz Perla, znajdziesz go pod adresem www.perl.com lub w archiwum CPAN, www.cpan.org. Na tych stronach odszukasz wszystko, co tylko może Ci być potrzebne. Specjalnie pominięto tutaj opis sposobu instalacji Perla w różnych systemach operacyjnych. Instalację opisano bardzo precyzyjnie na stronie Perla (na przykład instalację w systemie Unix przedstawiono na stronie www.perl.com/CPANlocal/doc/relinfo/INSTALL.html), a poza tym sposób instalowania kolejnych wersji się zmienia i zmiany te nie mogłyby znaleźć odbicia w tej książce. Warto zaznaczyć, że wiele książek zdezaktualizowało się właśnie dlatego, że podawano w nich dokładne instrukcje instalacyjne. Dotyczy to szczególnie książek o Javie, której instalacja zmieniała się niemalże natychmiast po pojawieniu się nowych wersji. Najnowszą wersję Perla można zwykle znaleźć, używając łącza „Get the latest version of Perl" (w witrynie www.perl.com), które przeniesie Cię na stronę, gdzie znajdziesz bezpośrednie łącza do najpopularniejszych wersji Perla, takich jak Perl for Win32 firmy ActiveState (pamiętaj o pobieraniu wersji co najmniej 5.005, aby dana wersja była zgodna z wersją dla systemu Unix i modułami dodatkowymi. Wcześniejsze wersje nieco się różnią). Jeśli instalujesz Perla w systemie wieloużytkownikowym, musisz mieć uprawnienia większe niż przy normalnej pracy. Zapisywanie skryptów Perla Skrypty Perla to zwykłe pliki tekstowe składające się z instrukcji tego języka oraz deklaracji (jak zobaczysz, wystarczy deklarować formaty i procedury). Do tworzenia skryptów Perla jest potrzebny edytor tekstowy, który umożliwia zapisywanie plików jako zwykłego tekstu. Zapisywanie plików jako zwykłego tekstu przekracza możliwości niektórych edytorów tekstowych. Problemem może być na przykład użycie programu Microsoft Word, choć w tym wypadku wystarczy zastosować okienko dialogowe Plik | Zapisz jako. Zasada ogólna jest taka, że jeśli wyświetlisz plik w wierszu poleceń (w systemach DOS i Windows jest to DOS), to nie zobaczysz na ekranie żadnych „krzaczków" (czyli dziwnych znaków). Oczywiście prawdziwa kontrola poprawności skryptu polega na tym, że sprawdza się, czy Perl jest w stanie skrypt odczytać i zinterpretować. Skrypty Perla mają zwykle rozszerzenie .pl, dlatego na przykład tworzony już niedługo program będzie się nazywał hello.pl. Skrypty Perla nie muszą mieć takiego rozszerzenia, wystarczy samo .p, a nawet można rozszerzenie całkiem pominąć. Jednak .pl jest często stosowane, a powszechnie wykorzystywana wersja Perla — ActiveState (przeznaczona dla 32-bitowych systemów Windows) wiąże to rozszerzenie z Perlem, dzięki czemu programy można uruchomić po dwukrotnym kliknięciu. Uwidacznianie Perla dla skryptów Jak się wkrótce okaże, skrypt Perla można uruchamiać na dwa sposoby. Jedną z możliwości jest wywoływanie interpretera tego języka (perl) bezpośrednio z wiersza poleceń: %perl hello.pl Można też spowodować, że skrypt sam będzie potrafił odnaleźć Perla, dzięki czemu możliwe stanie się uruchamianie skryptów jako samodzielnych poleceń: %hello.pl Jeśli zaś nie dodawałeś skryptom żadnego rozszerzenia, wywołanie może mieć postać: %hello W różnych systemach operacyjnych inaczej wskazuje się skryptom położenie Perla. Unix W systemie Unix interpreter Perla wskazuje się za pomocą dodania specjalnego pierwszego wiersza do skryptu (wiersz ten jest zbędny w przypadku jawnego wywoływania interpretera, co pokazano wcześniej): #!/usr/local/bin/perl # Użyj Perla Przy użyciu takiego wiersza ze składnią # ! bardzo ważne jest umieszczenie go na samym początku pliku. Wiersz w postaci pokazanej powyżej wskazuje standardowe położenie Perla w większości systemów Unix. Oczywiście, Perl może też zostać zainstalowany gdzie indziej, na przykład będzie dostępny jako /usr/bin/perl (w wielu systemach obydwie lokalizacje — /usr/bin/perl i /usr/local/bin/perl — wskazują to samo miejsce). Aby odnaleźć Perla w Twoim systemie Unix, możesz użyć polecenia which Perl. W celu wskazania, że ma być używany Perl w wersji 5, często stosuje się pierwszy wiersz w postaci: #!/usr/local/bin/perl5 # Użyj Perla 5 Istotne jest używanie przełącznika -w, który nakazuje interpreterowi pokazywanie w trakcie analizy kodu ostrzeżeń (interpreter Perla tak naprawdę kompiluje kod przy pierwszym jego załadowaniu, zatem ostrzeżenia pojawią się od razu, chyba że nakażesz późniejsze załadowanie kodu. Do ładowania kodu używa się instrukcji require, która zostanie omówiona w rozdziale 15): #!/usr/local/bin/perl5 -w # Użyj Perla 5, pokaż ostrzeżenia Z uwagi na to, że wiele wersji systemu Unix obcina wiersz # ! po pierwszych 32 znakach, możesz mieć kłopoty, jeśli ścieżka dostępu do Perla jest długa: #!/usr/local/bin/users/standard/build36/per!5 # Użyj Perla 5 W takim przypadku, a także wtedy, kiedy system nie rozpoznaje wiersza # !, można do wywołania Perla użyć interpretera powłoki (shell): #!/usr/bin/sh eval '/usr/local/bin/users/standard/build36/perl5 -wS $ ${l+"$@"}' if 0; W takiej sytuacji uruchamiamy polecenie eval wywołujące jawnie Perla, a dodatkowo ustawiając przełącznik -w, powodujemy wyświetlanie ostrzeżeń. Parametr $0 powinien zawierać pełną ścieżkę, jednak nie zawsze tak jest, wobec tego przełącznikiem -s nakazujemy Perlowi w razie potrzeby wyszukać skrypt. Dziwnie wyglądająca konstrukcja ${l + "$@"} służy do obsługi nazw plików zawierających spacje. Cały wiersz uruchamia skrypt Perla, ale nie zwraca żadnej wartości, gdyż wyrażenie if 0 nigdy nie jest prawdziwe. Przed uruchomieniem skryptu Perla w systemie Unix — przez podanie jedynie nazwy skryptu — konieczne jest uzyskanie prawa do wykonania pliku. Więcej na ten temat powiemy dalej, w punkcie „Uruchamianie skryptów Perla". Na początku wszystkich skryptów Perla można używać wiersza w postaci: #!/usr/local/bin/perl5 -w Jednak dla skrócenia zapisu (a także dlatego, że w wielu systemach operacyjnych to nie działa) zwykle wiersz ten w książce będzie pomijany. Czytelnicy, dla których okaże się to przydatne, mogą oczywiście umieszczać odpowiedni wiersz na początku swoich skryptów. MS-DOS W systemie MS-DOS trzeba zapewnić widoczność Perla, przekształcając skrypt na plik wsadowy BAT za pomocą narzędzia pl2bat.bat. Narzędzie to jest dostarczane wraz z Perlem ActiveState. Jeśli na przykład mielibyśmy skrypt hello.pl, zawierający: print "Hello!\n"; print "Press to continue..."; ; za pomocą narzędzia pl2bat.bat można byłoby przekształcić go na plik BAT, hello.bat, który można uruchamiać bezpośrednio z wiersza poleceń. W celu przekształcenia hello.pl na hello.bat należy zastosować polecenie: C:\>pl2bat hello.pl Oto wynikowy plik hello.bat: @rem = '—*-Perl-*-- @echo off if "%OS%" == "Windows_NT" goto WinNT perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9 goto endofperl :WinNT perl -x -S "%0" %* if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl if %errorlevel%==9009 echo You do not have Perl in your PATH. goto endofperl @rem '; #!perl #line 14 print "Hello!\n"; print "Press to continue.. . "; ; _END_ :endofperl Windows 95/98 i Windows NT Wersja ActiveState Perla dla Windows 95/98 i Windows NT modyfikuje rejestr w celu skojarzenia rozszerzenia .pl z interpreterem perl. W celu uruchomienia skryptu wystarczy dwukrotnie kliknąć skrypt. Otwiera się wówczas okno MS-DOS, wykonywany jest w nim skrypt i zanim da się cokolwiek przeczytać, okno MS-DOS jest z powrotem zamykane. Dalej w tym rozdziale, w punkcie „Unikanie natychmiastowego zamknięcia okna skryptu w Windows 95/98 i NT", znajdziesz sposób rozwiązania tego problemu. Macintosh Na komputerach Macintosh skrypty Perla są uruchamiane automatycznie — wystarczy je dwukrotnie kliknąć. Kod Perla: instrukcje i deklaracje Kod Perla składa się z instrukcji i deklaracji. Deklaracje wskazują Perłowi, jak dane konstrukcje programistyczne mają być używane. Można również deklarować zmienne, a konieczne są jedynie w odniesieniu do formatów i procedur. Więcej o deklaracjach powiemy dalej w tej książce. Instrukcje mogą mieć dwojaką postać: prostą i złożoną. Instrukcja prosta to wyrażenie wykonujące jakieś zadanie. W kodzie instrukcje proste kończą się średnikiem (;), jak na przykład poniższy fragmencik, w którym funkcja print wyświetla tekst Hello! oraz znak nowego wiersza \n (zobacz „Podstawowe formatowanie tekstu", dalej w tym rozdziale), powodujący przejście do następnego wiersza: print "Hello!\n"; Instrukcje złożone składają się z wyrażeń i bloków. Bloki w Perlu są ograniczane nawiasami klamrowymi { i }, mogą zawierać wiele instrukcji. Bloki stanowią też zakresy dla obiektów takich, jak zmienne (w zakresie można na przykład używać zmiennych, ale zagadnienie to omówimy dalej), przy czym po nawiasach klamrowych nie używa się średników. Oto przykład bloku, zawierający złożoną instrukcję w pętli for (więcej o pętli for w rozdziale 5): for ($loop_index = 1; $loop_index <=5; $loop_index++) { print "Hello"; print "there!\n"; } Zagadnienie związane: Pętla for Uruchamianie skryptów Perla Załóżmy, że mamy plik hello.pl zawierający skrypt Perla: #!/usr/local/bin/perl5 -w # Użyj Perla 5, włącz ostrzeżenia print "hello\n"; Jak taki skrypt uruchomić? Jest to podstawowe zadanie związane ze stosowaniem Perla, ale z uwagi na to, że istnieje wiele sposobów jego realizacji, przyjrzymy się im po kolei. Jeśli skrypt potrafi odnaleźć Perla Jeśli skrypt jest w stanie odnaleźć Perla (zajrzyj do punktu „Uwidacznianie Perla dla skryptów", wcześniej w tym rozdziale), łatwo go uruchomić. W Uniksie oznacza to, że pierwszy wiersz skryptu zawiera polecenie # ! /usr/local/bin/perl5 -w. Aby skrypt stał się plikiem wykonywalnym, należy użyć polecenia chmod: chmod +x hello.pl Trzeba też się upewnić, że skrypt należy do bieżącej ścieżki (PATH). W przypadku użycia powłoki csh, lub powłoki od niej pochodnej, trzeba sprawdzić polecenia set path w pliku .login. W przypadku stosowania powłoki sh — lub jej pochodnej — należy sprawdzić ustawienie zmiennej PATH w pliku .profile. Jeśli jako powłoka używany jest bash, trzeba sprawdzić ustawienia PATH w pliku .bashprofile. W ogóle powinniśmy wykorzystać technikę odpowiednią do stosowanej powłoki (interpretera poleceń). Wtedy można uruchomić skrypt z wiersza poleceń: %hello.pl W systemach Windows i Macintosh wystarczy dwukrotnie kliknąć plik skryptu. W Windows plikom należy nadawać rozszerzenie .pl, które jest używane przez oprogramowanie ActiveState do łączenia się z interpreterem Perla. W systemie MS-DOS, po użyciu pliku pl2bat.bat do konwersji skryptu na plik BAT (zajrzyj do punktu „Uwidacznianie Perla dla skryptów" wcześniej), wystarczy uruchomić plik wsadowy BAT: C:\hello.bat Aby użyć Perla z wiersza poleceń Aby wywołać jawnie interpreter Perla, trzeba upewnić się, że program perl znajduje się w ścieżce, a następnie użyć polecenia perl o składni wywołania podanej niżej. Przełączniki wskazywane w nawiasach kwadratowych są opcjonalne. Dalej w tym rozdziale, w punkcie „Użycie przełączników wiersza poleceń", poznamy znaczenie wszystkich przełączników: perl [ -sTuU ] [ -hv ] [ -V[:configvar] ] [ -cw ] [ -d[:debugger] ] [ -D[number/list] } l -pna ] [ -Fpattern ] [ -l[octal] ] { -O[octal] } [ -Idir ] [ -m[-]module } [ -M[-} 'module...' } [ -P ] [ -S ] [ -x[dir) ] [ -i[extension] ] [ -e 'command' ] [ -- ] [ programfile ] [ argument ] ... W przypadku takiego użycia polecenia perl interpreter Perla pobiera skrypt następująco: * z pojedynczego wiersza w przypadku użycia przełączników -e; * z pliku o nazwie podanej w wierszu poleceń; * zbierając kolejne wiersze ze standardowego wejścia, jeśli jako nazwę pliku podano kreskę (-). Teraz przyjrzymy się kolejnym metodom przekazywania skryptu. Używając przełącznika -e, można Perłowi przekazywać bezpośrednio kod, wiersz po wierszu (w niektórych systemach można użyć wielu przełączników -e, aby przekazać wiele wierszy kodu). Jak wspomniano we wprowadzeniu, wyniki działania programu będą zaznaczane kursywą: %perl -e "print 'Hello!\n'; " Hello! Można oczywiście także zapisać skrypt w pliku i przekazać interpreterowi nazwę tego pliku. Oto przykładowa treść pliku hello.pl (pomijamy wiersz # !, gdyż interpreter Perla wywołamy jawnie): print "Hello!\n"; Skrypt można wywołać, podając nazwę pliku: %perl hello.pl Hello! Można w końcu wpisać wielowierszowy skrypt jako nazwę pliku, podając kreskę (-). Jest to zresztą opcja domyślna, nawet jeśli zostanie pominięta kreska i napiszesz jedynie perl: %perl - W tym przypadku Perl będzie czekał na wpisanie całego skryptu: %perl - print "Hello!\n"; Aby uruchomić skrypt w Perlu, należy wpisać _END_: %perl - print "Hello!\n"; _END_ Hello! Jeśli użyta zostanie powyższa metoda uruchamiania skryptów Perla, cały skrypt wykona się od razu. W celu testowania wygodne jest uruchamianie kolejnych poleceń jedno za drugim, interaktywnie. Robi się to, używając mini interpretera poleceń Perla, który zostanie omówiony w następnym punkcie. Interaktywne uruchamianie skryptów Perla Podczas testowania programów przydatna jest możliwość interaktywnego uruchamiania kolejnych instrukcji Perla (w takiej kolejności, jak je wpisano w skrypcie) i od razu oglądania ich wyników. Żeby to zrobić, można napisać prosty interpreter Perla — oto prosty, ale działający przykład: #!/usr/bin/perl5 -w # Używamy Perla 5, włączamy ostrzeżenia #use strict; # Wymagane deklarowanie zmiennych itp. my $count = 0; # $count używana do parowania {}, () itd. my $statement = ""; # $stacement zawiera wielowierszowe instrukcje local $SIG(_WARN_} = sub {); # Pominiecie zgłaszania błędów while (<>) { # Pobieranie danych z klawiatury chomp; # Wyczyszczenie bufora wejściowego while (/{|\(|\[/g) {$count++}; # Szukaj {, ( itd. while (/}|\)|\]/g) {$count--}; # Paruj z ), ) itd. $statement .= $_; # Dołącz wprowadzone dane do instrukcji if (!$count) { # Ewaluuj tylko, jeśli sparowano {}, () eval $statement; # Ewaluuj instrukcję Perla if($@) (print "Błąd składniowy.\n"}; # Poinformuj o błędzie $statement = ""; # Wyczyszczenie bieżącej instrukcji $count = 0; # Wyzerowanie licznika {, (. } } Skrypt ten utworzy prosty interpreter Perla, który potrafi obsłużyć wiele instrukcji — nawet wielowierszowe instrukcje złożone. Użyta została funkcja Perla — eval, która interpretuje przekazane jej instrukcje. Zanim eval zacznie je przetwarzać, instrukcje wielowierszowe są zapisywane, a następnie zliczane są nawiasy otwierające ((, { i [) i zamykające (), } i ]), dzięki czemu wiadomo, kiedy instrukcja jest zakończona. Można na przykład nasz interpreter uruchomić i przypisać zmienną (więcej o zmiennych w następnym rozdziale): $text = "Hello!\n"; Wartość zmiennej możemy następnie wyświetlić: $text = "Hello! \n"; print $text; Od razu pokazane zostaną wyniki: $text = "Hello! \n"; print $text; Hello! Istnieje też możliwość sprawdzenia działania skryptów wielowierszowych (każda instrukcja jest wykonywana, kiedy zostanie skończona, zatem wyniki działania print są pokazywane natychmiast): $variablel = l ; $variable2 = 3; print $variablel + $variable2; 4 Można w końcu wpisywać wielowierszowe instrukcje złożone, jak poniższy fragment kodu, który składa się z pętli for: for ($loop_index = 1; $loop_index <=5; $loop_index++) { print "Hello!\n"; } Hello! Hello! Hello! Hello! Hello! Aby przerwać działanie interpretera, wystarczy wpisać exit. Tak tworzone samodzielnie interpretery Perla mogą być bardzo przydatne do testowania krótkich skryptów, bez konieczności zapisywania ich w pliku i uruchamiania w ten sposób. Pamiętaj, że podany interpreter stanowi tylko przykład i nie jest pełnym interpreterem — problemy mogą się pojawić przy próbie przetestowania polecenia eval, gdyż jest ono używane do uruchamiania skryptu. Użycie przełączników wiersza poleceń Przy stosowaniu polecenia perl dostępna jest niewiarygodna liczba przełączników (nawiasy kwadratowe, [ ], oznaczają, że dany przełącznik jest opcjonalny): perl [ -sTuU ] [ -hv ] [ -V[:configvar] ] [ -cw ] [ -d[:debugger] ] [ -D[number/list] ] [ -pna ] [ -Fpattern ] [ -l[octal] } [ -O(octal) ] [ -Idir ] [ -m[-]module ] [ -M(-] 'module..." ] [ -P l [ -S ] [ -x[dir] ] [ -i[extension] ] [ -e 'command' ] [ -- ] [ programfile ] [ argument ] ... Po co są wszystkie te przełączniki? Oto ich omówienie (niektórymi tematami, których przełączniki te dotyczą, zajmiemy się dopiero w dalszych rozdziałach): -0 [digits] — wskazuje separator rekordu wejściowego (zapisany też w specjalnej zmiennej $/) jako liczbę ósemkową; -a — przy użyciu opcji -a, -n lub -p włącza się tryb autosplit. Tryb ten powoduje rozbicie wierszy wejściowych (czyli rozdzielanie ich na słowa) i umieszczanie ich w specjalnej tablicy @F; -c — powoduje, że Perl sprawdza składnię skryptu i kończy swoje działanie, natomiast nie wykonuje skryptu; -d — uruchamia skrypt w debuggerze Perla.; -d:name — uruchamia skrypt pod kontrolą modułu debuggującego lub śledzącego, zainstalowanego jako Devel::name; -e commandline —opcji tej można użyć do wprowadzenia gotowego skryptu. W niektórych systemach za pomocą szeregu kolejnych opcji -e można tworzyć skrypty wielowierszowe; -Fpattern —jeśli uruchomiono -a, określa wzorzec rozdzielania; -h — wyświetla zestawienie dostępnych opcji; -i [extension] — mówi, że pliki przetwarzane przez konstrukcję <> (zajrzyj dalej do punktu „Odczyt wprowadzanych danych") mają być edytowane in situ: zmieniana ma być nazwa pliku wejściowego na inną, a następnie wyniki instrukcji print mają być zapisywane w pliku o pierwotnej nazwie; -Idirectory — nakazuje Perłowi wyszukiwać moduły w katalogu directory, -l[octnum] — wpływa na przetwarzanie końca wiersza. W przypadku użycia z opcjami -n lub -p automatycznie usuwa ze strumienia wejściowego zmienną $/ (jest to specjalna zmienna Perla zawierająca separator rekordu wejściowego, domyślnie znak nowego wiersza). Zmiennej $\ (separatorowi rekordu wyjściowego) przypisuje octnum jako liczbę szesnastkowaj znak ten będzie używany jako separator przez instrukcję print; -m[-]module lub M[-]module lub -M[-]'module ...' — przed uruchomieniem skryptu włącza do niego wskazany moduł (za pomocą instrukcji use module); -n — powoduje, że Perl otacza skrypt pętlą while (<>) (w punkcie „Odczyt wprowadzanych danych" znajdziesz więcej informacji o konstrukcji <>). Poniższy wiersz na przykład wyświetla zawartość pliku file.txt: perl -ne "print;" file.txt -P — uruchamia skrypt, poddając go uprzednio działaniu preprocesora języka C. Umożliwia to użycie dyrektyw C, takich jak #include czy #define, a także zastosowanie kompilacji warunkowej; -p — powoduje, że Perl otacza skrypt następującą pętlą: while (<>) { [tutaj umieszczany jest cały skrypt] } continue { ' print or die "-p destination: $!\n"; } -s — umożliwia analizowanie przełączników wiersza poleceń. Na przykład, jeśli skrypt wywołany zostanie z przełącznikiem -www, poniższa instrukcja wyświetli tekst „Znaleziono przeiącznik\n": if ($www) {print "Znaleziono przełącznik\n";} -S — powoduje, że do wyszukiwania skryptu używana jest zmienna środowiskowa PATH; -T — umożliwia śledzenie błędów. Opcja często jest używana w programach CGI; -u — powoduje, że Perl po skompilowaniu skryptu zapisuje na dysku obraz pamięci (core dump); -U — umożliwia Perlowi wykonywanie „niebezpiecznych" operacji, takich jak choćby zmiana nazw katalogów; -V — wyświetla zestawienie parametrów konfiguracyjnych Perla; -V:name — wyświetla wartość podanego parametru konfiguracyjnego Perla; -w — wyświetla ostrzeżenia (zajrzyj do następnego punktu); -x directory — informuje Perla, że skrypt jest włączony do komunikatu. Tekst nie będzie przetwarzany, aż do pierwszego wiersza zaczynającego się od # ! i zawierającego wyraz perl; -- — kreski te są opcjonalne, wskazuj ą koniec przełączników. Zagadnienie związane: Uruchamianie debuggera Dostępne polecenia debuggera Użycie przełącznika -w — ostrzeżenia Używając Perla, dobrze jest stosować przełącznik -w. Śmiało można by stwierdzić, że wielu użytkowników Perla ma na tym punkcie bzika. Tak czy inaczej, Autor także zaleca stosowanie ostrzeżeń. Przełącznik -w powoduje generowanie wielu różnych ostrzeżeń, między innymi o: * nazwach zmiennych, do których występuje tylko jedno odwołanie, * zmiennych skalarnych (prostych), które zostały użyte przed przypisaniem im wartości, * wielokrotnie zdefiniowanych procedurach, * odwołaniach do niezdefiniowanych uchwytów plików (w Perlu dostęp do plików jest realizowany przez uchwyty. Więcej informacji na ten temat znajdziesz w następnym punkcie), * uchwytach plików otwartych tylko do odczytu, do których mimo to próbuje się coś zapisywać, * wartościach używanych jako liczby, które jednak nie są liczbami, * użyciu tablic tak, jakby były to zmienne skalarne, * procedurach, w których rekurencja jest zagnieżdżona bardziej niż na 100 poziomów. Obsługa wejściowych i wyjściowych danych tekstowych przy pomocy standardowych uchwytów plików W Perlu wejście i wyjście traktowane są jako kanały (strumienie), których używa się za pośrednictwem uchwytów plików. Taki uchwyt jest po prostu wartością, która reprezentuje w Perlu plik, a jest określana w momencie otwierania owego pliku. Do danych tekstowych można użyć trzech predefiniowanych uchwytów plików: * STDIN — standardowy kanał wejściowy skryptu, * STDOUT — standardowy kanał wyjściowy, * STDERR — standardowy kanał wyjściowy błędów. Domyślnie powyższe uchwyty plików odpowiadają strumieniom terminala. Dalej w tym rozdziale używać będziemy właśnie powyższych predefiniowanych uchwytów. Wyświetlanie danych tekstowych Aby wydrukować dane tekstowe do pliku lub kanału STDOUT, używa się instrukcji print, która może mieć trojaką postać: print FILEHANDLE LISTA print LISTA print Jeśli nie zostanie podany uchwyt pliku, zostanie użyty STDOUT. Jeśli nie podana zostanie lista drukowanych treści (LISTA; lista może zawierać także tylko jeden element), funkcja print drukuje zawartość zmiennej specjalnej $_. Zmienna $_ zawiera domyślnie dane z kanału wejściowego (więcej informacji na ten temat znajdziesz dalej, w punkcie „Odczyt wprowadzanych danych"). Oto przykład, w którym drukujemy do kanału wyjściowego tekst „Hello!" i znak nowego wiersza: print "Hello!\n"; Hello! Funkcja print działa na takiej zasadzie, że pobiera listę elementów (listami w Perlu zajmiemy się dokładniej w następnym rozdziale). Oznacza to, że można podać listę elementów, które mają być wydrukowane, oddzielając je od siebie przecinkami: print "Hello ", "there!\n"; Hello there! Zwróć uwagę, że w Perlu można uzyskać bardzo ciekawe efekty, gdyż język ten udostępnia formaty i formatującą odmianę funkcji print, printf, która zostanie omówiona w rozdziale 11. Zagadnienie związane: printf — drukowanie formatowanych danych z list print — drukowanie do pliku Wyświetlanie numerów wierszy i nazw plików ze skryptami W skrypcie Perla można wyświetlać numery wykonywanych wierszy oraz nazwę pliku, a stosuje się do tego odpowiednio _LINE_ oraz _FILE_. Na przykład poniższy jednowierszowy skrypt wyświetli numer bieżącego wiersza, czyli l: %perl -e "print _LINE_;" 1 Wielokrotne drukowanie tekstu Jeśli trzeba wielokrotnie wydrukować jeden wyraz, można użyć operatora powtórzenia Perla, czyli x: print "Hello!\n" x 5; Hello! Hello! Hello! Hello! Hello! Można też narysować poziomą kreskę, powtarzając minusy: print "-" x 30 ------------------------------ Zagadnienie związane: Mnożenie i dzielenie Podstawowe formatowanie tekstu Stosując znaki specjalne, można w Perlu w podstawowym zakresie formatować tekst. Znak specjalny to znak poprzedzony lewym ukośnikiem (\), który wskazuje funkcjom takim, jak print, że mają obsłużyć tekst w jakiś szczególny sposób. Niektóre znaki specjalne i ich znaczenie podano w tabeli 1.1. Tabela 1.1 Znaki specjalne i ich znaczenie Znak specjalny Znaczenie \" cudzysłów podwójny \t tabulator \n znak nowego wiersza \r znak powrotu kursora \f znak nowej strony \b backspace (cofnięcie się o znak) \a sygnał dźwiękowy \e escape \033 znak zapisany ósemkowo \x1b znak zapisany szesnastkowo \c[ znak kontrolny Oto przykład pokazujący, jak wydrukować cudzysłów: print "\"Hello!\"\n"; "Hello!" Poniżej pokazujemy przykład użycia tabulatorów: print "Hello\tfrom\tPerl\n"; Hello from Perl Oto sposób utworzenia wielu wierszy przy pomocy znaku nowego wiersza, \n: print "Hello\nfrom\nPerl.\n"; Hello from Perl. Należy pamiętać, że są to tylko podstawowe możliwości formatowania, tymczasem Perl pozwala uzyskać znacznie więcej. Jest jeszcze jeden sposób korzystania z tekstu w Perlu — w końcu język ten pierwotnie stworzono właśnie do obsługi danych tekstowych. Przykład opisano w następnym punkcie, gdzie są omawiane dokumenty włączane. Zagadnienie związane: sprintf — formatowanie tekstu printf — drukowanie formatowanych danych z list Formaty: formatowanie danych wielowierszowych Wyświetlanie tekstu niesformatowanego: otwierane dokumenty Perla Dokumenty otwierane Perla umożliwiają wyświetlanie tekstu w takiej postaci, w jakiej został on wstawiony do skryptu. Aby zacząć taki dokument, trzeba wpisać « z jakimś znacznikiem — tutaj użyliśmy EOD: print <} { # Pobieranie danych z klawiatury chomp; # Wyczyszczenie bufora wejściowego while (/{|\(|\[/g) i$count++); # Szukaj {, ( itd. while (/}|\)|\]/g) )$count--); # Paruj z }, ) itd. $statement .= $_; # Dołącz wprowadzone dane do instrukcji if (!$count) { # Ewaluuj tylko, jeśli sparowano {}, () eval $statement; # Ewaluuj instrukcję Perla if($@) (print "Błąd składniowy.\n"}; # Poinformuj o biedzie $statement = ""; # Wyczyszczenie bieżącej instrukcji $count = 0; # Wyzerowanie licznika {, (. } } Odczyt wprowadzanych danych Wcześniej w tym rozdziale pokazano, jak użyć funkcji print do wydrukowania danych wynikowych. Pojawia się pytanie: jak jednak pobierać dane wejściowe? Aby odczytać je z pliku STDIN, wystarczy użyć pary nawiasów trójkątnych, < i >. Oto na przykład pętla while (omówiona w rozdziale 5.), która odczytuje dane wprowadzone przez użytkownika, zapisuje poszczególne wiersze w zmiennej $temp i je drukuje: while ($temp = ) { print $temp; } Kiedy uruchomisz ten skrypt, wpiszesz na przykład „Hello!" i wciśniesz ENTER, skrypt zwróci wpisane dane: Hello! Hello! Istnieje krótsza metoda zapisania tego samego —jak zwykle w Perlu (funkcjonuje nawet pewien slogan związany z Perlem: „Da się to zrobić też inaczej"). Informacje na ten temat znajdziesz w następnym punkcie. Zagadnienia związane: Pętla while Użycie zmiennej domyślnej $_ Kiedy używa się konstrukcji , nie przypisując wyniku do żadnej zmiennej, Perl automatycznie wynik ten przypisze zmiennej $_. Wiele funkcji Perla stosuje tę zmienną jako zmienną domyślną, wykorzystywaną, kiedy nie podano źródła informacji jawnie. Jedną z takich funkcji jest choćby print: jeśli funkcji tej nie zostaną podane parametry, wydrukuje zawartość $_ (istnieje w Perlu jeszcze wiele innych zmiennych specjalnych, na przykład $! zawierająca ostatni błąd. Więcej o nich w rozdziale 9.). Można także w ogóle pominąć STDIN pomiędzy nawiasami —jeśli użyje się tylko < i >, właśnie STDIN jest plikiem domyślnym. Warto wspomnieć, że w Perlu istnieje mnóstwo ustawień domyślnych, które ułatwiają pracę ekspertom, ale stanowią zagadkę dla nowicjuszy. Przyjrzyj się, jak wygląda skrócony kod z poprzedniego punktu: while (<>) { print; } Ten króciutki fragment można zapisać też następująco: while($_ = ) { print $_; } Zagadnienie związane: $! — ostatni błąd Czyszczenie danych wejściowych Dane wejściowe, odczytane z STDIN, zawierają wszystko, co tylko wpisał użytkownik, w tym znak nowego wiersza na końcu. Używając funkcji chop lub chomp, można się pozbyć znaku nowego wiersza. Oto sposób użycia chop: chop VARIABLE chop LISTA chop Funkcja ta odrzuca z tekstu ostatni znak i go zwraca. Jeśli pominięto zmienną VARIABLE, dane są odrzucane ze zmiennej $_. Spójrzmy na poniższy skrypt: while (<>) { print; } Kiedy drukowane są kolejne wiersze wejściowe, na końcu każdego z nich znajduje się znak nowego wiersza. Jeśli jednak użyjemy funkcji chop, po poszczególnych wierszach znak nowego wiersza nie będzie się już pojawiał: while (<>) { chop; print; } Oprócz funkcji chop można też użyć chomp: chomp VARIABLE chomp LISTA chomp Funkcja ta jest bezpieczniejszą wersją chop, usuwa wszelkie zakończenie wiersza pasujące do wartości $/ — specjalnej zmiennej Perla, zawierającej separator rekordów wejściowych (domyślnie znak nowego wiersza). Funkcja ta zwraca łączną liczbę usuniętych znaków, a używa jej się zwykle do usuwania z rekordu wejściowego znaków nowego wiersza. Jeśli pominięta zostanie zmienna VARIABLE, znaki usuwane są z $_. Unikanie natychmiastowego zamknięcia okna skryptu w Windows 95/98 i NT Jeśli używasz Perla w systemie Windows 95/98 lub NT, zapewne zauważyłeś pewną kłopotliwą cechę tego środowiska: kiedy dwukrotnie klikniesz plik z rozszerzeniem .pl, pokazuje się okno MS-DOS, skrypt się wykonuje i okno natychmiast jest zamykane. W związku z tym nie ma możliwości odczytania wyników działania skryptu. Z problemem tym można sobie poradzić, wymuszając na skrypcie oczekiwanie na dane wejściowe z klawiatury — po zakończeniu wykonywania tego skryptu. Wystarczy na końcu dodać następujące dwa wiersze: print "Press to continue..."; Wyniki pokazano na rysunku 1.1 — skrypt się wykonał i oczekuje na wciśnięcie Enter. Można powyższe dwa wiersze jeszcze skrócić, gdyż <> ma takie samo znaczenie, jak : print "Hello!\n"; print "Press to continue..."; <> Rozdział 2. Zmienne skalarne i listy W skrócie W tym rozdziale zaczniemy w Perlu przetwarzać dane. Język Perl jest w przetwarzaniu danych wyjątkowo skuteczny i dlatego temu tematowi poświęcimy wiele miejsca w książce. W tym rozdziale skupimy się na dwóch rodzajach danych: zmiennych skalarnych i listach. Zmienne skalarne Zmienne skalarne w wielu językach programowania są określane jako zmienne proste (w Perlu często nazywa się je skalarami), zawierają pojedynczą daną: liczbę, tekst lub wskaźnik Perla (więcej o wskaźnikach w rozdziale 8.). Zmienne te nazywamy skalarnymi, aby odróżnić je od konstrukcji, w których można przechowywać więcej danych jednocześnie, jak tablice. W języku naukowym skalar oznacza wartość liczbową, podczas gdy wektory wartości takich mogą mieć więcej. Zresztą w programowaniu tablice jednowymiarowe nazywa się czasem wektorami. Zmienną skalarną poprzedza się znakiem dolara, $. Może to wyglądać nieco dziwnie, ale taki właśnie zapis jest w Perlu stosowany, dlatego żadna zmienna nie pomyli się ze słowem zarezerwowanym Perla. Do przypisywania wartości zmiennym skalarnym stosuje się operator przypisania, =: $scalarl = 5; Sscalar2 = "Hello there! ' Zagadnienie związane: Tworzenie wskaźnika Listy Jak to wynika z samej ich nazwy, listy są po prostu listami elementów danych. Poszczególne elementy nie muszą być skalarami, same mogą być tablicami lub tablicami asocjacyjnymi (jednymi i drugimi zajmiemy się w następnym rozdziale), a nawet kolejnymi listami. W przeciwieństwie do skalarów i tablic nie istnieje specjalny typ danych dla list, ale sama idea listy jest w Perlu bardzo istotna — list będziemy używać w całej tej książce. Lista to struktura łącząca ze sobą elementy danych. Listę tworzy się, rozdzielając poszczególne elementy przecinkami. Oto przykład wydrukowania elementów listy "H", "e", "l", "l", "o" za pomocą funkcji pnnt, która z założenia może jako argument przyjąć listę: print "H", "e", "l", "l", "o"; Hel l o Zwróć uwagę, że w tym przypadku nie przypisaliśmy ciągu kolejnych liter zmiennej, gdyż w Perlu nie istnieje listowy typ danych. Funkcje języka Perl można podzielić na dwie grupy: funkcje spodziewające się parametrów skalarnych i spodziewające się parametrów w postaci listy (choć wiele funkcji potrafi obsłużyć parametry obu rodzajów). Skąd Perl wie, kiedy traktować dane jako skalary, a kiedy jako listę? Decyzja podejmowana jest na podstawie kontekstu, przy czym istnieją dwa podstawowe jego rodzaje: kontekst skalarny i kontekst listowy. W tym podziale można iść jeszcze dalej, na przykład kontekst numeryczny i kontekst tekstowy to dwa przykłady kontekstu skalarnego. W praktyce oznacza to tyle, że trzeba zapamiętać, które funkcje są funkcjami skalarnymi, a które listowymi. Wskaż typ funkcji za każdym razem przy jej pierwszym przedstawieniu — oto przykład dotyczący funkcji map: map BLOK LISTA Więcej o kontekstach skalarnym i listowym powiemy w następnym rozdziale. Powyższe wprowadzenie na razie nam wystarczy — teraz czas zająć się kotłem. Zagadnienia: Czym jest zmienna skalarna? Zmienna skalarna to nazwa miejsca na dane w pamięci, przy czym te dane mogą być liczbą, ciągiem znaków lub wskaźnikiem (wskaźnik zachowuje się tak, jak adres jakiegoś innego elementu danych). Zmienne skalanie Perla pozwalają też, zapisywać inny typ danych — typ nieokreślony (zajrzyj do punktu „Użycie wartości nieokreślonej: undef, dalej w tym rozdziale). Warto zauważyć, że zmienne skalarne mogą zawierać dane wielu różnych typów, oznacza, że skalary w Perlu nie mają typu (poza wskaźnikami, w przypadku który typy są ściśle przestrzegane) — czyli całkiem inaczej niż w językach takich jak C. To, kiego typu dane są w zmiennej — liczba, tekst czy też inne — określa się w Perlu w ; leżności od kontekstu. Nazwy zmiennych skalarnych Nazwa zmiennej skalarnej zawierać może litery, cyfry i podkreślenia. Zaczynać się j* nak musi znakiem $, co pozwala unikać konfliktu ze słowami kluczowymi Perla. Ni wa zmiennej może być długa. Wspomniana długość zależy od używanego systemu, zwykle jest to co najmniej 255 znaków. Z uwagi na to, że nazwy zmiennych skalarnych zaczynają się od znaku $, dzięki czei nie zachodzi możliwość pomyłki ze słowami zarezerwowanymi, można zapisywać je n łymi literami, tak zresztą robi większość programistów. Prawie wszystkie słowa k czowe Perla są zapisywane też małymi literami, wyjątkiem są uchwyty plików (_ STDIN) i funkcje (jak początek bloku, BEGIN, w pakiecie). Należy pamiętać, że wielki liter w nazwach zmiennych jest istotna — $ zmienna l to co innego, niż $ Zmienna l. Po początkowym znaku $ może wystąpić dowolna litera lub podkreślenie. Nazwy żmii nych można zaczynać nawet cyfrą, ale w takim przypadku nazwa może zawierać tylko cyfry. Istnieje też możliwość używania znaków innych niż znaki alfanumeryc; i podkreślenie, ale w takim przypadku za znakiem $ musi wystąpić już tylko jeden zi — przykładem są zmienne wbudowane Perla, jak na przykład $-.1 Wprawdzie zmienne skalarne dzięki początkowemu znakowi $ nie mylą się ze słowami kluczowymi Perla, ale w języku tym tworzy się też inne identyfikatory, które nie muszą być poprzedzone żadnym specjalnym znakiem, na przykład uchwyty plików czy etykiety. Aby uniknąć konfliktu nazw w takim przypadku, najlepiej dodać do wspomnianych identyfikatorów kilka wielkich liter. Znak $ — rozpoczynający nazwy zmiennych skalarnych w Perlu —jest nazywany i różnikiem początkowym. Oto inne wyróżniki przedrostkowe Perla wraz z ich znaczeniei $ — zmienne skalarne, % — zmienne asocjacyjne (czyli tablice asocjacyjne, omówione w następnym rozdziale), @ — tablice, & — procedury, * — typy ogólne (zatem *myvar może oznaczać dowolnego typu myvar, na przykład @myvar, %myvar i tak dalej. Więcej informacji w rozdziale 3.). Przypisywanie wartości skalarnych Jak umieścić dane w zmiennej skalarnej? Wystarczy użyć operatora przypisania. Oto na przykład przypisanie zmiennej $variable1 wartości 5: $variable1 = 5 Tak samo przypisuje się wartości tekstowe: $variable1 = "Hello there!"; Operatora przypisania można użyć do przypisania dowolnej wartości lvalue. Jeśli nie wiesz, co owo lvalue oznacza, zajrzyj do następnego punktu. Co to jest lvalue? lvalue to coś, co może być celem przypisania. Nazwa pochodzi od „left value" (lewa wartość), czyli wartość, która występuje po lewej stronie przypisania: $variable1 = 5; lvalue zwykle reprezentuje miejsce w pamięci na dane, gdzie dane można zapisać, podając nazwę tego miejsca. lvalue może być dowolną zmienną, a tak naprawdę lvalue może być nawet samo przypisanie, jak w poniższym przykładzie, w którym odrzucamy wartość z $input, a nie wartość zwróconą przez operację przypisania: chop ($input = 123); print $input; 12 Konstrukcję tę zauważysz nieraz w Perlu, kiedy wczytywane są dane wejściowe, następnie odrzucane jest ich zakończenie, a wynik zostaje w $input — wszystko to da się zapisać w jednym wierszu: chop ($input = <>); Użycie liczb jako wartości skalarnych W Perlu obsługiwanych jest szereg formatów numerycznych, pokazanych w tabeli 2.1. Zwróć szczególną uwagę na format Underlines (z podkreśleniem), gdyż umożliwia on grupowanie cyfr po trzy, dzięki czemu prostsze jest odczytywanie liczb takich jak l 234 567: $variable1 = l_234_567; Tabela 2.1. Numeryczne typy danych Typ Przykład Floating 1.23 Hex 0x123 Integer 123 Octal 0123 Scientific 1.23E4 Underlines 1_234_567 Użycie wartości nieokreślonej: undef Perl — oprócz liczb, tekstu i wskaźników — pozwala przechowywać w zmiennych skalarnych także wartość nieokreśloną, nazywaną undef. Wartość ta zwracana jest przez niektóre funkcje, a można ją sprawdzać, stosując funkcję defined. Jeśli wartość undef sprawdzić bezpośrednio, zostanie ona zinterpretowana w kontekście numerycznym jako 0, a jako pusty znak "" w kontekście tekstowym. Funkcja undef pozwala przypisać zmiennym wartość undef. Oto przykład — najpierw przypisujemy zmiennej $variable1 wartość 5: $variablel = 5; Jeśli do zmiennej tej użyjemy funkcji undef, wartość zmiennej będzie nieokreślona: $variablel = 5; undef $variable1; Teraz za pomocą funkcji defined możemy sprawdzić, czy wartość zmiennej $variable1 została określona: $variablel = 5; undef $variablel; if (defined $variable1) { print "Określono wartość \$variablel.\n"; } else { print "Nie określono wartości \$variablel.\n"; } W tym przypadku uzyskamy wynik: Nie określono wartości $variable1. Deklarowanie stałej Nie ma w Perlu typu dla stałych liczbowych, ale można taki typ zdefiniować samemu (potrzebne techniki omówiono w rozdziale 8. oraz w punkcie „Typy ogólne" rozdziału 3.). Typ ogólny może dotyczyć dowolnej zmiennej, a jego zapis zaczyna się gwiazdką(*). Aby utworzyć stałą, należy typowi ogólnemu przypisać wskaźnik. W poniższym przykładzie definiujemy stałą określającą dopuszczalną liczbę plików: *MAXFILES = \100; Stałej tej można używać jako $MAXFILES (zwróć uwagę na wielkie litery, których u żywamy w celu odróżnienia stałej od zmiennej): *MAXFILES = \100; print "$MAXFILES\n"; Jeśli z kolei spróbujemy $MAXFILES przypisać jakąś wartość, otrzymamy błąd: *MAXFILES = \100; print "$MAXFILES\n"; $MAXFILES = 101; Powyższy kod da następujący rezultat: 100 Modification of read-only value attempted at constant.pl line 3. Obsługa w Perlu wartości logicznych Jest jeszcze jeden ważny rodzaj danych, o którym trzeba wspomnieć: wartości logicznie. Instrukcje sterujące wykonaniem programu, takie jak if, używają wyrażeń logicznyc h, których wartością jest true lub false. W Perlu 0 oznacza fałsz, zaś każda inna wartość oznacza prawdę. Fakt, że dowolna wartość niezerowa jest prawdą, okazuje się szczególnie wygodny w wyrażeniach warunkowych. Na przykład poniższa pętla while działa dlatego, że <> zawsze coś zwróci, nawet jeśli użytkownik wpisze pusty wiersz (w tym przypadku <> zwróci znak nowego wiersza): while(<>) { print; } Przekształcanie liczb ósemkowych, dziesiętnych i szesnastkowych W Perlu liczby ósemkowe wyróżnia się, podając na ich początku cyfrę 0, na przykład 0123, zaś liczby szesnastkowe poprzedza się wyrazem 0x, na przykład 0x1AB. Stosując liczby zapisane w różnych systemach, warto wiedzieć, jak przekształcać je na liczby dziesiętne, a także jak przekształcać liczy dziesiętne do innych systemów. Szesnastkowe na dziesiętne Aby przekształcić liczbę szesnastkowąna dziesiętną, użyj funkcji hex: print hex 0xlAB; 1063 Jeśli dla funkcji hex nie podasz wartości, zostanie zastosowana zmienna domyślna, $_. Dziesiętne na szesnastkowe Aby przekształcić liczbę dziesiętną na szesnastkową w postaci znaku, należy użyć funkcji Perla sprintf z konwersją %x: print sprintf "%1x", 16; 10 Ósemkowe na dziesiętne Aby przekształcić liczbę ósemkową na dziesiętną, należy wykorzystać funkcję oct: print = oct 10; 8 Jeśli nie podamy przekształcanej wartości, zostanie użyta zmienna domyślna $_. Dziesiętne na ósemkowe Aby liczbę dziesiętną przekształcić na jej zapis ósemkowy, podobnie jak w przypadku zapisu szesnastkowego, należy zastosować funkcję printf, tym razem z konwersją %o: print sprintf "%1o", 16; 20 Zagadnienie związane: sprintf—formatowanie tekstu Zaokrąglanie liczb Aby liczbę zaokrąglić do wskazanej ilości miejsc po przecinku, używa się funkcji sprintf. W celu — na przykład — zaokrąglenia liczby do dwóch miejsc po przecinku, użyj formatu "%.2f". Oto wynik zaokrąglenia 3.1415926 do dwóch miejsc po przecinku: print sprintf "%.2f", 3.1415926; 3.14 Pokazane tutaj działanie nie odrzuca końcowych cyfr, ale naprawdę liczby zaokrągla. Oto wynik zaokrąglenia tej samej liczby do czterech miejsc po przecinku — ostatnia cyfra 5 jest zamieniana na 6: $variable1 = sprintf "%.4f", 3.1415926; print $variable1; 3.1416 W poprzednich dwóch przykładach pokazano zaokrąglanie liczb i wyświetlanie wyniku. Można by się zastanawiać, jak liczbę zaokrąglić i później użyć jej jako danej numerycznej. Trzeba pamiętać, że Perl obsługuje dane tak, jak to wynika z kontekstu, zatem jeśli skalar będzie traktowany przez programistę jako liczba, tak samo potraktuje go Perl (o ile będzie to możliwe). W poniższym przykładzie zaokrąglamy liczbę i zapisujemy ją w zmiennej $variable1: $variable1 = sprintf "%.2f", 3.1415926; Następnie wartość ze zmiennej $variable1 traktujemy jako liczbę, wykonując na niej działanie arytmetyczne, w tym przypadku dodajemy do liczby 0,01: $variable1 = sprintf "%.2f", 3.1415926; $variable1 += .01; Następnie wynik wyświetlamy: $variable1 = sprintf "%.2f", 3.1415926; $variable1 += .01; print $variable1; 3.15 Zagadnienie związane: sprintf—formatowanie tekstu Użycie w zmiennych skalarnych tekstów Zmienne skalarne mogą oprócz liczb zawierać także tekst: $variable1 = "Hello!"; Warto zauważyć, że w celu złączenia (konkatenacji) wyrazów nie dodaje się ich, jak w innych językach. Operator + nie zadziała: $variable1 = "Hello "; $variable2 = "there\n"; print $variablel + $variable2; # wyrazy nie zostaną połączone! Można użyć natomiast operatora konkatenacji Perla, czyli kropki: $variable1 = "Hello "; $variable2 = "there\n"; print $variablel . $variable2; Hello there Wyrazy można zapisywać, stosując cudzysłowy pojedyncze lub podwójne: $variable1 = "Hello."; $variable2 = 'Hello again.'; Między tymi dwiema metodami istnieje pewna różnica. Perl określa wartość zmiennych i niektórych wyrażeń zamkniętych w podwójne cudzysłowy (szczegóły znajdziesz w następnym punkcie). W przypadku użycia cudzysłowu pojedynczego nic nie jest interpretowane, Perl całość traktuje jako literał. W tekście można też używać znaków specjalnych, pokazanych w tabeli 2.2. Aby na przykład umieścić w tekście podwójny cudzysłów, należy zastosować zapis \": print "Powiedziałem \"Hello\"."; Powiedziałem "Hello" Tabela 2.2. Znaki specjalne Znak Znaczenie \' pojedynczy cudzysłów \" podwójny cudzysłów \t tabulator \n nowy wiersz \u zamiana następnej litery na wielką \l zamiana następnej litery na małą \U zamiana wszystkich dalszych znaków na wielkie litery \L zamiana wszystkich dalszych znaków na małe litery \Q dodanie lewego ukośnika do wszystkich dalszych znaków niealfanumerycznych \E koniec \L, \U i \Q \r powrót kursora \f wysunięcie strony \b backspace \a brzęczyk \e escape \033 znak zapisany ósemkowo \x1b znak zapisany szesnastkowo \c[ znak kontrolny Stosowanie interpolacji w tekście Jeśli tekst zawierający nazwę zmiennej jest otoczony podwójnym cudzysłowem, Perl podstawia do niego wartości wszystkich występujących w nim zmiennych. Jeśli na przykład zmienna $text zawiera słowo Hello: $text = "Hello"; można tej zmiennej użyć w podwójnych cudzysłowach, a Perl podstawi za zmiennąjej wartość, czyli w naszym przypadku tekst Hello: $text = "Hello"; print "Perl mówi: $text!\n"; Perl mówi: Hello! Takie podstawianie części tekstu nazywa się w Perlu interpolacją. W pokazanym przykładzie interpolowana została zmienna $text, ujęta w podwójne cudzysłowy. Jeśli jednak użyjemy cudzysłowów pojedynczych, żadna interpolacja nie zostanie wykonana: $text = "Hello"; print 'Perl mówi: $text!\n'; Perl mówi: $text!\n Oznacza to, że cudzysłowów pojedynczych należy używać, kiedy nie chce się, żeby Perl wyliczał wyrażenia. Co jednak zrobić, gdy zmienna ma być interpolowana jako część innego słowa? Jeśli na przykład $text zawiera tekst „nie", który to tekst chcemy dodać do słowa „szczęśliwy"? Nie można użyć oczywiście wyrażenia $textszczęśliwy, gdyż Perl będzie szukał zmiennej $textszczęśliwy, nie będzie zaś interpolował zmiennej $text. Do wyróżnienia interpolowanej części możemy użyć nawiasów klamrowych { }: $text = "nie"; print "Nie bądź taki ${text}szczęśliwy."; Nie bądź taki nieszczęśliwy. Można jeszcze używać odwróconego pojedynczego cudzysłowu ("), który powoduje, że Perl przekazuje tak zaznaczone polecenie systemowi operacyjnemu. Na przykład w systemie Unix można uruchomić polecenie uptime, które pokazuje, jak długo już działa komputer: $uptime = 'uptime'; print $uptime; 4 :29pm up 18 days, 21:22, 13 users, load average: 0.30, 0.39, 0.42 W podobny sposób działa ta technika w systemie DOS, gdzie można uruchomić polecenie dir: $$dirlist = 'dir'; print $dirlist; Directory of C:\perlbook\temp . 10-07-99 4:02p . .. 10-07-99 4:02p .. TEMP PL 3.535 10-07-99 4:06p T.PL Programiści często używają interpolacji do konkatenacji tekstu: $a = "Hello"; $b = "tnere"; print "$a $b\n"; Hello there Skomplikowane interpolacje Stosując operator konkatenacji, można także interpolować wyniki procedury: $string = $textl . mysubroutine($data) . $text2; Z drugiej strony, jeśli dobrze się zaplanuje, można procedurę napisać tak, aby uzyskać jej wartość interpolowaną w podwójnych cudzysłowach za pomocą mechanizmu ${} (więcej o procedurach w rozdziale 7.). Załóżmy na przykład, że należy interpolować wynik procedury getmessage. Można to zrobić tak, jak pokazano niżej (zwróć uwagę, że konieczne jest jawne użycie wyróżnika &, czyli pierwszego i zwykle opcjonalnego znaku w nazwie procedury): print "${&getmessage}"; W tym przypadku podstawiany jest tekst zwracany przez procedurę getmessage. Cała sztuczka polega na ustawieniu wartości zmiennej w procedurze oraz zwrócenie tej nazwy: print "${&getmessage}"; sub getmessage { $msg = "Hello!"; return "msg" } ; Teraz print "${&getmessage}"; da taki wynik, o jaki chodzi: print "$(&getmessage)"; sub getmessage { $msg = "Hello!"; return "msg" } ; Hello! Jednak rozwiązanie takie działa tylko w odniesieniu do samodzielnie napisanych procedur. Jest ogólniejsza metoda, która wymusza na Perlu ewaluację wyniku procedury przez użycie wskaźników. Tę kwestię jednak omówimy po zapoznaniu się z samymi wskaźnikami w rozdziale 8. Oto sposób interpolowania wartości zwracanej przez funkcję skalarną: $string = "text ${\(scalarfunction data)} text"; Jeśli chcemy na przykład użyć funkcji uc zmieniającej litery na wielkie (i zapomnieliśmy, że ten same efekt można uzyskać, stosując znak specjalny \u), można to zapisać następująco: print "${\(uc \"x\")}"; X Zwróć uwagę, że konieczne było poprzedzenie cudzysłowów lewym ukośnikiem, gdyż w przeciwnym razie Perl miałby problemy z potraktowaniem tekstu jako jednej całości. Jeśli procedury zwracają listy, można użyć tablicy anonimowej: $string = "text @{[listfunction data]} text"; Zagadnienie związane: Definiowanie procedur Tworzenie wskaźników na tablice anonimowe Obsługa cudzysłowów i samodzielnych słów W Perlu cudzysłowy otaczające słowa są opcjonalne, jeśli słowa te mogą być zinterpretowane tylko w jeden sposób. Na przykład poniżej widzimy przypisanie tekstu do zmiennej $text, wobec czego cudzysłowy można pominąć: $text = Hello; Kiedy wyświetlimy zawartość zmiennej $text, uzyskamy spodziewany wynik: $text = Hello; print $text; Hello Tekst może składać się z pojedynczych słów nieujętych w nawiasy, nazywanych samodzielnymi słowami. Zwróć uwagę, że jeśli użyto więcej niż jednego słowa, przestają one być słowami samodzielnymi: $text = Hello there!; # niedobrze print $text; # nie działa Czasem samodzielne słowa mogą zostać pomylone z etykietami lub uchwytami plików, gdyż żadne z nich nie wymagają przedrostkowego wyróżnika, takiego jak $. Tę tolerancję Perla dla samodzielnych słów można wyłączyć, co spowoduje, że jeśli samodzielne słowa nie będą mogły być zinterpretowane jako nazwy procedur, Perl wygeneruje ostrzeżenie: use list 'subs'; W Perlu cudzysłowy można nie tylko pomijać, ale również można użyć Perla do automatycznego dodawania cudzysłowów przy wykorzystaniu konstrukcji z tabeli 2.3. Tabela 2.3. Konstrukcje podstawiane Konstrukcja Wynik Interpolacja? Zastępuje q// '' nie literał qq// "" tak literał qx// `` tak polecenie qw// () nie lista słów // m// tak wzorzec s/// s/// tak podstawienie y/// tr/// nie translacja Jeśli na przykład należy wyświetlić tekst „Powiedziałem "Hello".", można użyć cudzysłowów za backlashem: print "Powiedziałem \"Hello\"." Powiedziałem "Hello". Aby jednak uniknąć zbyt wielu lewych ukośników (co niektórzy uważają za chorobę Perla), można użyć konstrukcji qq//, która już zajmie się podwójnymi cudzysłowami: print qq/Powiedziałem "Hello"./; Powiedziałem "Hello". Tak naprawdę do ujmowania tekstu nie trzeba używać znaków / i /, można zastosować niemalże dowolne inne znaki, byle tylko znak początkowy był taki sam, jak końcowy. W poniższym przykładzie użyto kresek pionowych, |: print qq|Powiedziałem "Hello".|; Powiedziałem "Hello". Można nawet wykorzystać nawiasy, które zwykle służą do ujmowania parametrów przekazywanych procedurom: print qq(Powiedziałem "Hello".); Powiedziałem "Hello". W tym przypadku nawiasy pełnią rolę ograniczników dla qq, nie służą zaś do ujęcia parametrów procedury (zresztą w Perlu zawsze istnieje więcej niż jeden sposób realizacji poszczególnych zadań, można więc nawet pominąć nawiasy przy wywoływaniu procedury, jeśli nie spowoduje to pomylenia innych części danej instrukcji). Czym jest lista? Perl umożliwia zestawianie wartości skalarnych (a także innych typów danych, jak asocjacje i tablice) w listy. Warto zauważyć, że listy są w Perlu bardzo ważne, zresztą wbudowane funkcje Perla dzielą się na dwie zasadnicze grupy: funkcje, które mogą obsługiwać skalary, oraz funkcje, które mogą obsługiwać listy (choć niektóre funkcje potrafiąjedno i drugie). Nie istnieje w Perlu specjalny typ danych listowych. Jednak funkcjonuje operator listy, którym jest para nawiasów, pozwalająca stworzyć listę przez umieszczenie w tych nawiasach elementów rozdzielanych przecinkami. Na przykład wyrażenie (l, 2, 3) daje w wyniku trzyelementową listę. Funkcja print jest funkcją listową — przekazuje się jej listę elementów, które zostaną złączone w jeden tekst. Na przykład po wywołaniu: print (l, 2, 3); wyświetlone zostanie: 123 Można nawet ominąć nawiasy (w takiej sytuacji Perl potraktuje print niejako funkcję, lecz jako operator listy): print l, 2, 3; 123 Zagadnienie związane: Najwyższy priorytet: termy i lewe operatory list Odwoływanie się do elementów listy przez indeks Po utworzeniu listy warto się odwoływać do jej poszczególnych elementów, stosując nawiasy kwadratowe [ ], które można uważać za operator indeksowania listy. Jeśli na przykład lista składa się z liter a, b i c (których nie trzeba ujmować w nawiasy, gdyż Perl zinterpretuje je jako samodzielne słowa), do litery b można odwołać się jako do elementu l (liczymy od zera): $variable1 = (a, b, c)[l]; Kiedy wartość zmiennej wydrukujemy, otrzymamy oczekiwany wynik: $variable1 = (a, b, c)[1]; print $variable1; Warto zwrócić uwagę, że można indeksować nawet listę zwróconą przez funkcję, co daje łatwy sposób korzystania z wyników wtedy, gdy chce się korzystać tylko ze skalarów. Przypisywanie listy innej liście Jedną listę można przypisać innej, stosując operator przypisania, =. Oto przykład, w którym przypisujemy elementy listy ($c, $d) odpowiednim elementom listy ($a, $b): ($a, $b) = ($c, $d) ; W ten sposób można traktować listy jako zestaw elementów lvalue. Dwie listy mogą nawet zawierać zmienne, jak w pokazanym przykładzie, gdzie wymieniana jest wartość zmiennych $a i $b przez przypisanie list, bez pośrednictwa tymczasowych zmiennych: ($a, $b) = ($c, $d) ; Przypisywane sobie listy mogą nawet różnić się wielkością, jak w poniższym przykładzie, gdzie $a i $b otrzymają wartości odpowiednio pierwszych dwóch elementów z dłuższej listy: ($a, $b) = (l, 2, 3); print $a ; 1 print $b; 2 Odwzorowanie listy Aby wykonać tę samą operację na wszystkich elementach listy, używamy funkcji map: map BLOK LISTA map WYRAŻENIE, LISTA Funkcja ta ewaluuje kod z BLOKU lub WYRAŻENIA dla każdego elementu LISTY (podstawiając wartość każdego elementu zmiennej $_) i zwraca listę wyników. Aby na przykład zmienić wszystkie elementy na małe litery za pomocą funkcji lc, należy zapisać: ($a, $b) = map (1c, A, B) ; print $a, $b; ab Łączenie listy w ciąg znaków Aby wszystkie elementy listy złączyć w pojedynczy ciąg znaków, używa się funkcji join: join WYRAŻENIE, LISTA Funkcja rozdziela znaki z LISTY za pomocą WYRAŻENIA i łączy je w pojedynczy ciąg znaków. Aby na przykład połączyć elementy listy ("12", "00", "00") w j eden wyraz 12:00:00, zastosujemy następujący kod: print join (":", "12", "00", "00"); 12:00:00 Nie jest konieczne umieszczanie poszczególnych elementów listy w cudzysłowach, ale jeśli znaki te zostaną potraktowane jako samodzielne słowa, Perl będzie próbował zinterpretować 00 jako liczbę, pominie zero wiodące i w wyniku otrzymamy 12:0:0. Oczywiście, nie jest konieczne wskazywanie znaku łączącego elementy listy, można — jak w poniższym przykładzie — przekazać znak pusty: print join ("", H, e, l, l, o); Hello Przekształcanie tekstu w listę Do rozbicia tekstu na liczbę można użyć wbudowanej funkcji Perla split: split /WZORZEC/, WYRAŻENIE, LIMIT split /WZORZEC/, WYRAŻENIE split /WZORZEC/ split Funkcja split przekształca WYRAŻENIE przy każdym wystąpieniu WZORCA. Jeśli nie podano WYRAŻENIA, używana jest zmienna $_, pominięcie WZORCA powoduje przekształcenie w białe znaki. Jeśli podano LIMIT (musi być dodatni), split rozbija tekst na nie więcej — niż podana liczba — elementów. Oto przykład rozbicia tekstu „H,e,l,l,o" na listę i wydrukowania wyniku: print split /,/, "H,e,l,l,o"; Hello Sortowanie list Do posortowania listy służy funkcja sort: sort PROCEDURA LISTA sort BLOK LISTA sort LISTA Funkcja ta sortuje podaną LISTĘ i zwraca listę posortowaną. PROCEDURA określa nazwę procedury zwracającej wynik porównania dwóch elementów tak, jak robią to operatory <=> i cmp (więcej na ten temat w rozdziale 4.). Można też podać kod porównania w BLOKU. Jeśli nie zostanie podana PROCEDURA ani BLOK, sort użyje standardowego porządku. Oto na przykład sortowanie listy ("c","b","a"): print sort ("c", "b", "a"); abc Można też użyć operatora porównania wyrażeń cmp, ale wynik będzie taki sam: print sort {$a cmp $b} ("c", "b", "a"); abc Tak samo możemy nakazać użycie porządku malejącego: print sort {$b cmp $a) ("c", "b", "a"); cba Istnieje również możliwość wykorzystania operatora porównania liczb <=>: print sort {$a <=> $b} (3, 2, l); 123 Można nawet kod porównujący wstawić do procedury: sub myfunction { return (shift(@_) <=> shift(@_)); } print sort (myfunction($a, $b) ) (3, 2, l) ; 123 Zagadnienie związane: Użycie operatorów równości Odczytywanie parametrów procedury Odwracanie listy Aby odwrócić kolejność elementów listy, stosuje się funkcji reverse: reverse LISTA Oto sposób użycia tej funkcji do odwrócenia elementów listy (l, 2, 3) print reverse (l, 2, 3); 321 Wybieranie elementów z listy Używając funkcji grep, można utworzyć podlistę elementów listy, spełniających pewne warunki: grep BLOK LISTA grep WYRAŻENIE LISTA Funkcja grep — podobna do polecenia grep systemu Unix — ewaluuje BLOK lub WYRAŻENIE dla każdego elementu LISTY (przypisując każdy element zmiennej $_) i zwraca listę wartości z tych elementów, dla których wyrażenie było prawdziwe. Funkcja grep zwykle służy do dopasowywania wzorca, jak w poniższym przykładzie, gdzie tworzymy listę elementów niebędących znakiem x: print grep(!/x/, a, b, x, d); abd Więcej informacji o wzorcach znajdziesz w rozdziale 6., ale jak już widać, grep jest potężnym narzędziem do tworzenia podlist składających się z elementów spełniających pewne kryteria. Zagadnienie związane: Wyrażenia regularne Jak rozumieć konteksty skalarny i listowy? Dwa podstawowe konteksty Perla to kontekst skalarny i kontekst listowy, a zrozumienie różnicy między nimi jest bardzo istotne. Z oboma tymi kontekstami będziemy stale spotykać się dalej w tej książce. Kiedy Perl spodziewa się listy, dane traktuje w kontekście listowym, kiedy zaś oczekuje danych skalarnych, traktuje dane w kontekście skalarnym. Wobec tego, jeśli Perl zakłada, że dalej będzie lista, to dane traktuje jako listę. Jeśli natomiast zakłada, że dalej będzie skalar, dane traktuje jako dane skalarne. Innymi słowy, sposób traktowania danych w Perlu jest określony niejawnie, w zależności od kontekstu, nie można zatem jawnie zdefiniować tego w kodzie. Przy użyciu funkcji pobierających lub zwracających listy, parametry są automatycznie traktowane jako listy. W kontekście skalarnym listy często stają się skalarami (zajrzyj do następnego punktu), zaś w kontekście list skalary stają się niejednokrotnie jednoelementowymi listami. Zwróć jednak uwagę, że nie istnieje w Perlu reguła opisująca różnice zachowania się wyrażenia w kontekście listowym i kontekście skalarnym. Jeśli na przykład kontekst zostanie zmieniony na skalarny, niektóre operatory zwrócą długość listy, jaka zostałaby zwrócona w kontekście listowym, inne zaś zwrócą pierwszą wartość z listy, kolejne zwrócą ostatnią wartość z listy, a jeszcze inne nawet zwrócą liczbę udanych operacji. Brzmieć to może nieco skomplikowanie, ale zwykle nie wy stępuj ą przełączenia między kontekstami skalarnym i listowym, więc takie wątpliwości rzadko będą występowały. Wymuszanie kontekstu skalarnego Funkcja scalar wymusza interpretowanie WYRAŻENIA w kontekście skalarnym (zwróć uwagę, że brakuje odpowiedniego operatora wymuszającego interpretowanie w kontekście listowym). Oto składnia funkcji scalar: scalar WYRAŻENIE Załóżmy, że mamy listę (l, 2, 3): print (l, 2, 3); 123 Jeśli użyjemy funkcji scalar, wymuszona zostanie interpretacja w kontekście skalarnym, co oznacza, że zwrócony zostanie ostatni element listy: print scalar (l, 2, 3); 3 Funkcja scalar zwraca ostatni element listy, naśladując w ten sposób operator „przecinek", który robi to samo — zwraca wartość ostatniego wyrażenia z listy przecinkowej: $a = (l, 2, 3); print $a; 3 Można wymusić też użycie kontekstu skalarnego, przypisując listę do skalara (na przykład $variable = (l, 2, 3)),anawet — choć jest to mniej eleganckie rozwiązanie — wykonując na liście operację skalarną (na przykład dodając operatorem + do listy wartość 0). Rozdział 3. Tablice i asocjacje W skrócie W tym rozdziale zostanie przedstawione zestawianie danych w dwie ważne struktury danych: tablice i asocjacje. Zobaczymy też, jak stosować inną ważną konstrukcję: typy ogólne. Tablice Tablice układają listę skalarów według indeksu liczbowego i umożliwiają odwoływanie się do skalarów przez ten indeks. Indeks jest bardzo ważny, gdyż zmniejszając go lub zwiększając, można przetwarzać całą tablicę. Tablicę możemy utworzyć, przypisując listę zmiennej tablicowej, czyli zmiennej o nazwie zaczynającej się od znaku @: @tablica = (l, 2, 3), Do poszczególnych skalarnych elementów tablicy można odwołać się podając indeks elementu w nawiasach kwadratowych [ ], a także podstawiając $ zamiast @, aby zaznaczyć, że chodzi o wartość skalarną (należy pamiętać, że w Perlu indeksy zaczynają się od zera): print $array[0]; 1 W tym rozdziale zajmiemy się zwykłymi, jednowymiarowymi tablicami Perla. W rozdziale 13., omawiając struktury danych, przedstawimy także tablice dwuwymiarowe. Asocjacje Asocjacje często nazywa się też tablicami asocjacyjnymi, a używa się w nich (zamiast indeksów) kluczy. Stosując asocjacje, łączy się wartość z kluczem tekstowym: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash(picie} = szampan; Następnie do wartości można odwoływać się, podając klucze: print $hash(kanapka); hamburger Zestawianie danych w asocjacje zwykle jest bardziej intuicyjne, niż używanie zwykł; tablic, gdyż dane można wybierać, podając klucz. Jest to doskonały sposób tworze rekordów danych. Typy ogólne Typy ogólne to kolejna specyficzna cecha Perla. Wyróżnikiem przedrostkowym t} ogólnego jest gwiazdka *, która jednocześnie jest znakiem ogólnym używanym p wybieraniu plików. To słuszne rozwiązanie, gdyż typu ogólnego można użyć jako a su wszystkich typów związanych z daną nazwą. Jeśli na przykład mamy zmienne $data i @data: $data = "Tutaj są dane."; @data = (l, 2, 3) ; dzięki zastosowaniu typu ogólnego, możemy odwołać się do tych zmiennych przez inną nazwę: *alsodata = *data; Od tej chwili $alsodata jest aliasem $data, zaś @alsodata jest aliasem @data: print "$alsodata\n"; Tutaj są dane. I to tyle wstępnych informacji. W następnej części zajmiemy się szczegółowo nowymi typami. Zagadnienia: Tworzenie tablic Zmienna tablicowa zawsze zaczyna się znakiem @. Tablicę można tworzyć, przypi jąć zmiennej listę: @tablica = (l, 2, 3); Aby zobaczyć wynik, wystarczy wyświetlić nową tablicę: @tablica = (l, 2, 3), print @tablica; 123 Funkcja print traktuje tablicę jak listę i łączy elementy w 123 — w punkcie „Wyświetlanie tablic", dalej w tym rozdziale, opisano efektowniejsze sposoby pokazywania zawartości tablic. Do poszczególnych elementów tablicy można odwoływać się przez indeks podawany w nawiasach kwadratowych, przy czym trzeba poprzedzić nazwę tablicy znakiem $, gdyż poszczególne elementy tablicy są już skalarami: @tablica = (l, 2, 3); print $tablica[0]; 1 Oczywiście w tablicy można trzymać nie tylko liczby, ale także dowolne inne wielkości skalarne, choćby tekst: @tablica = ("raz", "dwa", "trzy"); print @tablica; razdwatrzy Z uwagi na to, że przy obsłudze list Perl pomija białe znaki (w tym znaki nowego wiersza), można zastosować najwygodniejszy układ graficzny przypisania wartości tablicy: @tablica = ( "raz", "dwa", "trzy", "cztery", "pięć", "sześć" ); print gtablica; razdwatrzyczterypięćsześć Można też użyć operatora powtórzenia x, jak poniżej, gdzie tworzymy tablicę stu zer: Stablica = (0) x 100; Warto również skorzystać z operatorów cytowania, na przykład qw: @tablica = qw(raz dwa trzy); print @tablica; razdwatrży Oprócz — opisanych do tej pory — technik służących do tworzenia lub dodawania elementów do tablic, można użyć funkcji push i unshift (wspomniane zagadnienie omówimy dalej w tym rozdziale). Warto pamiętać, że choć indeksacja tablic domyślnie zaczyna się od zera, to tę bazę można zmienić, ustawiając odpowiednio wartość zmiennej specjalnej Perla $ [. Jednak stosowanie tej zmiennej nie jest w Perlu zalecane (w innych językach, jeśli jakieś metody nie są zalecane — na przykład w Javie — oznacza to, że mogą stać się niedostępne. W Perlu tak nie jest). Użycie tablic Po utworzeniu tablicy można się odwoływać do poszczególnych jej elementów jako do wartości skalarnych. Wystarczy poprzedzić nazwę tabeli znakiem $ i podać w nawiasach kwadratowych indeks liczbowy: @tablica = ("raz", "dwa", "trzy"); print $tablica[l]; dwa Z uwagi na to, że do sięgania do elementów tablicy można używać indeksów, tablice mogą działać niczym swego rodzaju słowniki, jak w poniższym przykładzie, w którym przekształcamy wartość dziesiętną podaną przez użytkownika (od 0 do 15) na cyfrę szesnastkową: while (<>) { @array = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); $hex = $array[$_]; print "$hex\n"; } Fakt, że tablice pozwalają numerować dane, jest bardzo ważnym narzędziem programistycznym, gdyż pozwala to utworzyć pętlę iteracyjną po całej tablicy, jak w poniższym przykładzie, gdzie za pomocą indeksu przechodzimy po wszystkich elementach tablicy: @array = ("raz", "dwa", "trzy"); for (@loop_index = 0; $loop_index <= $#array; $loop_index++) { print $array[$loop_index]; } razdwatrzy Warto zapamiętać specjalną wartość $#array, która zwraca ostatni indeks w tablicy darray (zajrzyj do punktu „Określanie wielkości tablicy", dalej w tym rozdziale). Zagadnienie związane: Pętla for Dokładanie elementów na koniec tablicy i ich usuwanie Zawartość tablic można zmieniać, nie tylko używając przypisania list, ale również stosując funkcje push i pop. Funkcja push dodaje na koniec tablicy wartość lub wartości: push TABLICA, LISTA Funkcja push dokłada LISTĘ na koniec TABLICY. Wielkość TABLICY zwiększa się o długość LISTY. Funkcja pop zdejmuje wartość z tablicy: pop TABLICA pop Powyższa funkcja zdejmuje (czyli usuwa i następnie zwraca) ostatnią wartość w tablicy, skracając tym samym tablicę o jeden element. Oto przykład, w którym do tablicy dokładamy elementy: push(@array, "raz"); push(@ array, "dwa"); push(@array, "trzy") ; print $array[0]; raz A oto przykład zdjęcia elementu tablicy: @array = ("raz", "dwa", "trzy"); $variable1 = pop(@array); print $variable1; trzy Przesuwanie elementów tablicy Funkcje shift i unshift służą do wykonywania z lewym końcem tablicy tego, co push i pop robią z jej prawym końcem. Oto składnia shift: shift TABLICA shift Funkcja ta przesuwa pierwszą wartość tablicy poza tablicę i zwraca tę wartość, przez co skraca tablicę o jeden element, zaś wszystkie elementy poza pierwszym są przesuwane na miejsce o jeden wcześniejsze. Poniżej znajduje się składnia funkcji unshift: unshift TABLICA, LISTA Funkcja ta działa odwrotnie niż shift: dodaje na początku TABLICY LISTĘ, zwraca nowe elementy tablicy. Oto przykład, w którym za pomocą funkcji shift pobieramy element tablicy: @array = ("raz", "dwa", "trzy"); $variable1 = shift(@array); print $variable1; raz Określanie wielkości tablicy Załóżmy, że mamy tablicę o nazwie garray — wtedy wyrażenie $#array pozwala określić największy indeks tej tablicy. Nazwa @array w żaden sposób nie jest wyróżniona, bo jeśli tablica będzie się nazywać @telefony, to ostatni indeks zostanie określony wyrażeniem $#telefony. Jeśli na przykład mamy taką oto tablicę: @array = (l, 2, 3) ; to w celu określenia długości tej tablicy — trzeba dodać l do $#array: @array = (l, 2, 3); print "\3array ma" . (#array + 1) . "elementów."; @array ma 3 elementów. Dodanie jedynki do $#array wynika stąd, że pierwszym indeksem jest zero. Użycie tablicy w kontekście skalarnym także zwraca jej wielkość. Aby zastosować tablicę w kontekście skalarnym, należy na przykład przeprowadzić na niej jakąś operację arytmetyczną, niezmieniającą wyniku, choćby dodać zero: @array + 0 Można też — co wygląda bardziej profesjonalnie — użyć funkcji scalar: @array = (l, 2, 3); print: "\@array ma" . scalar (@array) . "elementów."; @array ma 3 elementów. Można w końcu przypisać tablicę zmiennej skalarnej: @array = (l, 2, 3); $variable = @array; print "\@array ma $variable elementów."; @array ma 3 elementów. Powiększanie i zmniejszanie tablic Aby zmienić liczbę elementów w tablicy, wystarczy po prostu zmienić wartość ostatniego indeksu tej tablicy, czyli $#array. Oto przykład: @array = (l, 2, 3); $#array = 10; $array[5] = "Oto nowy element!"; print "$array[5]\n"; Oto nowy element! Tak naprawdę, przy odwołaniu się do nieistniejącego elementu tablicy, Perl automatycznie rozszerza tablicę, tworząc elementy o indeksach mniejszych i równych indeksowi żądanemu: @array = (l, 2, 3); $array[5] = "Oto nowy element!"; print "$array[5]\n"; Oto nowy element! Wreszcie można usunąć wszystkie elementy tablicy, ustawiając jej wielkość na wartość ujemną: $#array = -1; Łączenie dwóch tablic Dwie tablice można połączyć za pomocą przypisania list. W poniższym przykładzie łączymy 0arrayl z @array2, tworząc nową tablicę, @bigarray: @arrayl = (l, 2, 3); @array2 = (4, 5, 6); @bigarray = (@arrayl, @array2); Nowej tablicy można używać w dowolny sposób: print $bigarray[5]; 6 Pobieranie warstw tablic Warstwa tablicy to jej część, którą tworzy się za pomocą operatora zakresu. Operator zakresu wygląda tak: [x..y] — w tym przypadku odwołujemy się do elementów o numerach x, x+l i tak dalej, aż do y. Oto przykład, w którym tworzymy podtablicę @array2, składającą się z drugiego i trzeciego elementu tablicy @array: @array = (l, 2, 3, 4, 5, 6, 7, 8, 9, 10); @array2 = @array [2 . . 3]; print join(", ", @array2); 3, 4 Pętle na tablicach Jak to pokazywano już wcześniej w tym rozdziale, można użyć pętli for do przejścia po wszystkich elementach tablicy, odwołując się do nich przez indeks: @array = ("raz", "dwa", "trzy"); for($loop_index = 0; $loop_index <= $#array; $loop_index++) { print $array[$loop_index]; } razdwatrzy Do wykorzystania wszystkich elementów tablicy można też użyć pętli foreach: @array =(1,2,3,4,5); foreach $element (@array) { print "$element\n"; } 1 2 3 4 5 Możliwe jest nawet tworzenie pętli obejmującej jednocześnie kilka tablic — wystarczy utworzyć listę tablic (tablice zostaną zamienione na jedną listę): @array = (l, 2, 3) ; @array2 = (4, 5, 6) ; foreach $element (@array, @array2) { print "$element\n"; } 1 2 3 4 5 Oprócz pętli foreach można też zastosować pętlę for (zresztą tak naprawdę foreach i for to ta sama pętla): @array = (l, 2, 3, 4, 5) ; for $element (@array) { print "$element\n"; } 1 2 3 4 5 W razie potrzeby można nawet wykorzystać pętlę for bez odwoływania się do konkretnych elementów w pętli, natomiast przy użyciu zmiennej domyślnej $_: @array = (l, 2, 3, 4, 5); for (@array) { print; } 12345 Jak widać, pętle na tablicach konstruujemy na wiele różnych sposobów, zaś wybór jednego z nich zależy od tego, co chcemy osiągnąć. Zagadnienie związane: Pętla foreach Wyświetlanie tablic Kiedy należy tablicę po prostu wyświetlić, można przekazać ją funkcji print następująco: @array = ("raz", "dwa", "trzy"); print "Oto tablica: ", @array", ".\n"; Oto tablica: razdwatrzy. Jednak print jest funkcją listową, więc traktuje ona tablicę jako listę i wyświetla wszystkie elementy obok siebie, przez co uzyskujemy dziwne słowo „razdwatrzy". Lepszym pomysłem jest użycie interpolacji w podwójnych cudzysłowach, przy czym nazwa tablicy także musi się znaleźć w tych cudzysłowach: @array = ("raz", "dwa", "trzy"); print "Oto tablica: @array.\n."; Oto tablica: raz dwa trzy. W tym przypadku Perl interpolował tablicę, używając domyślnego separatora wyjściowego ze specjalnej zmiennej $,. W sytuacji, gdy chcemy wyświetlić tablicę jako listę przecinkową, zmiennej $, trzeba przypisać przecinek, ale wówczas otrzymamy taki oto wynik: @tablica = ("raz", "dwa", "trzy"); $, = ","; print "Oto tablica: ", @tablica, ".\n"; Oto tablica: ,raz,dwa, trzy,. Lepszym rozwiązaniem jest użycie funkcji join, utworzenie na podstawie tablicy tekstu i jawne rozdzielenie poszczególnych elementów tablicy przecinkami: @array = (l, 2, 3, 4, 5, 6, 7, 8, 9, 10); print join(", ", @array); l, 2, 3, 4, 5, 6, l, 8, 9, 10 Można też oczywiście jawnie utworzyć pętlę for czy foreach: @array = ("raz", "dwa", "trzy"); foreach $element (@array) { print "Bieżący element = $element\n"; } Bieżący element = raz Bieżący element = dwa Bieżący element = trzy W końcu do poszczególnych elementów tablicy dostaniemy się, stosując ich indeksy, co oznacza, że można tablicę wyświetlić praktycznie w dowolnym układzie. Warstwy tablic Warstwy tablic umożliwiają dodawanie elementów listy do tablicy, przy czym istniejące elementy tablicy mogą być zastępowane. Do złączania tablic używa się funkcji splice o następującej składni: splice TABLICA, OFSET, DŁUGOŚĆ, LISTA splice TABLICA, OFSET, DŁUGOŚĆ splice TABLICA, OFSET Funkcja splice usuwa DŁUGOŚĆ elementów — od OFSET poczynając — i ewentualnie zastępuje je LISTĄ. W kontekście listowym funkcja splice zwraca elementy usunięte z tablicy, a w kontekście skalarnym — ostatni usunięty element (lub wartość undef, jeśli nie usunięto żadnych elementów). Jeśli pominięto DŁUGOŚĆ, splice usuwa wszystkie elementy: od OFSET do końca tablicy. Oto przykład, w którym do tablicy zawierającej elementy „raz" i „dwa" dodajemy element „trzy": @array = ("raz", "dwa"); splice(@array, 2, 0, "trzy"); print join(", ", @array) ; raz, dwa, trzy W następnym przykładzie doklejamy nową tablicę na koniec starej: @array = ("raz", "dwa"); @array2 = ("trzy", "cztery"); splice(@array, 2, 0, @array2); print join(", ", @array); raz, dwa, trzy, cztery Można także zastępować wybrane elementy tablicy, dlatego w następnym przykładzie ostatni element pierwszej tablicy, „zero", zastąpimy drugą tablicą zawierającą elementy „dwa", „trzy" i „cztery": @array = ("raz", "zero"); @array2 = ("dwa", "trzy", "cztery"); splice(@array, 2, l, @array2); print join(", ", @array); raz, dwa, trzy, cztery Odwracanie tablic Aby odwrócić kolejność elementów tablicy, wystarczy użyć funkcji reverse: @New = reverse @array; Sortowanie tablic W celu posortowania tablicy używa się funkcji sort: @new = sort ($a <=> $b) @array; Sortowanie może być dowolnie skomplikowane; poniżej posortujemy tablicę w porządku malejącym: @new = sort {b <=> $a} @array; Zagadnienie związane: Sortowanie list Tworzenie asocjacji Asocjacje często są nazywane tablicami asocjacyjnymi, zresztą ta druga nazwa zdaje się lepiej je opisywać, gdyż do ich wartości odwołuje się tak, jak do wartości tablic, ale zamiast indeksu używa się klucza (czyli tekstu) związanego z daną wartością. Dzięki odwoływaniu się do wartości za pośrednictwem kluczy, a nie liczb, przechowywanie danych jest prostsze do zrozumienia niż w przypadku tablic. Jednak trzeba pamiętać, że trudno może być z kolei zrealizować pętlę po wszystkich danych. Zmienne asocjacyjne poprzedza się znakiem procentu %, jak w poniższym przykładzie, gdzie utworzono pustą asocjację: %hash = (); Tak jak w przypadku tablic, tak i przy asocjacjach wyróżnik $ jest używany do wybierania poszczególnych elementów. Oto przykład umieszczenia w asocjacji kilku elementów (pierwszym kluczem jest owoc, odpowiada mu wartość jabłko; drugim kluczem jest kanapka, odpowiada mu hamburger i tak dalej): %hasz = (); $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; Zwróć uwagę na nawiasy klamrowe, które są używane do odwołania się do poszczególnych pozycji — w tablicach były to nawiasy kwadratowe. Do poszczególnych elementów asocjacji można się odwoływać, podając ich klucze: %hash = () ; $hash{owoc} = jabłko; $hash{ kanapka} = hamburger; $hash{picie} = szampan; print $hash{kanapka}; hamburger W ten sposób można tworzyć asocjacje zawierające klucze i odpowiadające tym kluczom wartości. Przed rozpoczęciem wypełniania asocjacji danymi nie trzeba nawet tworzyć asocjacji pustej. Przy próbie użycia nieistniejącej asocjacji, Perl automatycznie ją utworzy (na tym między innymi polega wygoda Perla: rzeczy działają w nim tak, jak można by się spodziewać), zatem poniższy kod zadziała równie dobrze jak poprzedni: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; print $hash{kanapka}; hamburger Warto znów przypomnieć, że Perl w przypadku wczytywania nowych elementów tablicy pomija białe znaki, wobec czego można wygodnie je zapisywać: @array = ( "raz", "dwa", "trzy", "cztery", "pięć", "sześć", ); W ten sam sposób tworzy się asocjacje — podając pary klucz + wartość: %hash = ( owoc, jabłko, kanapka, hamburger, picie, szampan, ); print "$hash{owoc}\n"; jabłko Istnieje zresztą odpowiednik przecinka: =>. Użycie tego operatora czyni relację między kluczem a wartością czytelniejszą, więc tworzenie asocjacji często zapisuje się następująco: %hash = ( owoc => jabłko, kanapka => hamburger, picie => szampan, ); print "$hash{owoc}\n"; jabłko Operator => sam z siebie nic specjalnego nie robi, jest to po prostu inna nazwa operatora „przecinek" (no, z jedną tylko różnicą: niezależnie od tego, co znajdzie się po jego lewej stronie, jest interpretowane jako tekst). Na przykład instrukcja: print "x"=>"y"=>"z"; xyz jest równoważna instrukcji: print "x", "y", "z"; xyz Można też używać kluczy zawierających spacje, jak poniżej, gdzie jako klucz wykorzystamy wyrażenie lody w waflu: $hash2{ciastko} = czekoladowe; $hash2{place} = jagodowy; $hash2{'lody w waflu'} = orzechowe; Do takich kluczy można również normalnie się odwoływać: print "$hash{'lody w waflu'}\n"; orzechowe Do tworzenia kluczy stosujemy także interpolację w podwójnych cudzysłowach, ale możemy też oczywiście zastosować bezpośrednio zmienną: $value = $hash{$klucz}; Asocjacje doskonale nadają się do przechowywania danych, ale trzeba pamiętać, że nie można się do nich odwoływać bezpośrednio za pomocą indeksów liczbowych. Nie oznacza to oczywiście, że nie możemy stworzyć na asocjacji pętli (zajrzyj dalej w tym rozdziale do punktu „Przetwarzanie asocjacji w pętli"). Użycie asocjacji Po utworzeniu asocjacji można się od jej wartości odwoływać: $value = $hash{$klucz}; Oprócz tego możemy w asocjacji umieszczać elementy, bezpośrednio je przypisując, jak w tym przykładzie z poprzedniego punktu: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; print $hash{kanapka}; hamburger Kiedy asocjacja zostanie użyta w kontekście listy, wszystkie pary klucz+wartość będą wstawione do listy. Jeśli asocjacja zostanie użyta w kontekście skalarnym, a są w niej jakiekolwiek pary klucz+wartość, zwrócona zostanie wartość true. Dodawanie elementów do asocjacji Aby do asocjacji dodać nowy element (czyli parę składającą się z klucza i wartości), wstarczy użyć operatora przypisania: %hash = () ; $hash($klucz) = $value; $hash($klucz2) = $value2; Asocjacje można tworzyć, używając przypisania list. Tak samo do asocjacji dodajemy nowe elementy — w poniższym przykładzie do %hash dodamy nową parę: %hash = ( owoc => jabłko, kanapka => hamburger, picie => szampan, ); %hash = (%hash, dressing, 'blue cheese'); print "$hash{dressing} \n"; blue cheese Przykład ten działa dlatego, że operator list, ( ) , przekształca %hash nalistę, a następnie rozszerza tę listę o jedną parę klucz+wartość. Jako że asocjację przekształcamy w listę przed przypisaniem listy, nie można w tym przypadku użyć skróconego operatora +=: %hash += (dressing, 'blue cheese'); # nie działa! Zagadnienie związane Przypisania Sprawdzanie, czy istnieje dany element asocjacji Aby sprawdzić, czy w asocjacji istnieje dany element, można użyć funkcji exists. Poniżej na przykład zastosowano funkcję exists do sprawdzenia, czy istnieje w asocjacji %hash element o kluczu warzywo: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; if (exists($hash{"warzywo"})) { print "Element istnieje."; } else { print "Element nie istnieje."; } Element nie istnieje. Usuwanie elementów asocjacji Aby usunąć z asocjacji element, wystarczy użyć funkcji delete. Oto przykład, w którym usuwamy element i następnie sprawdzamy funkcją exists, czy jeszcze istnieje: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; delete($hash{"owoc"}); if (exists($hash{"owoc"})) { print "Element istnieje."; } else { print "Element nie istnieje.” } Element nie istnieje. Przetwarzanie asocjacji w pętli Pętle na asocjacjach realizujemy na wiele sposobów. Jeśli chodzi o pobranie wszystkich elementów, czyli par klucz+wartość, używa się funkcji each. Oto przykład, w którym najpierw utworzymy asocjację: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; Teraz możemy pobierać pary klucz+wartość — wystarczy użyć listowego operatora przypisania z funkcją each, jak poniżej: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; while(($key, $value) = each(%hash)) { print "$key => $value\n"; } picie => szampan kanapka => hamburger owoc => jabłko Użycie funkcji each jest wygodne, gdyż otrzymuje się zarówno klucz, jak i odpowiadającą mu wartość. Warto zauważyć, że elementy asocjacji nie są zwracane w kolejności ich wprowadzania. Dzieje się tak dlatego, że Perl zapisuje elementy asocjacji według pewnych wewnętrznych reguł, które są zoptymalizowane pod kątem minimalizacji zużycia pamięci i prostoty dostępu do danych. Sortowanie asocjacji będzie omówione w tym rozdziale, w punkcie „Sortowanie asocjacji". Można też w celu utworzenia pętli na asocjacji zastosować instrukcję foreach. W następnym przykładzie użyjemy funkcji keys, która zwraca wszystkie klucze z asocjacji: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; foreach $key (keys %hash) { print $hash{$key} . "\n"; } szampan hamburger jabłko Z asocjacji można pobrać nie tylko klucze, ale także wartości. Umożliwia to realizację pętli analogicznej do poprzedniej: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; foreach $value (values %hash) { print "$value\n"; } szampan hamburger jabłko Jak widać, Perl oferuje programiście wiele metod realizacji pętli na asocjacji, mimo że asocjacje nie mają indeksów liczbowych, które pozwoliłyby dostać się bezpośrednio do kolejnych elementów, jak w przypadku tablic. Wyświetlanie asocjacji Asocjację można wyświetlić, stosując interpolację w podwójnych cudzysłowach: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; print "@{[%hash]}\n"; picie szampan kanapka hamburger owoc jabłko Warto zauważyć, że asocjacja została wyświetlona jako lista: kolejno po sobie następują pary klucz+wartość. Lepszym rozwiązaniem jednak może być użycie funkcji each: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; while (($key, $value) = each %hash ) { print "$key: $value\n"; } picie: szampan kanapka: hamburger owoc: jabłko Istnieje jeszcze wiele innych metod realizacji pętli na asocjacji, a więcej informacji na ten temat w punkcie „Przetwarzanie asocjacji w pętli", wcześniej w tym rozdziale. Sortowanie asocjacji Asocjację można posortować za pomocą funkcji sort. Oto przykład sortowania asocjacji według kluczy: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; foreach $key (sort keys %hash) { print "$key => $hash{$key}\n" } picie => szampan kanapka => hamburger owoc => jabłko Można też asocjację posortować według wartości, a nie według kluczy: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; foreach $key (sort values %hash) { print "$value\n"; } jabłko hamburger szampan Łączenie dwóch asocjacji Aby połączyć ze sobą dwie asocjacje, używa się przypisania list. Załóżmy na przykład, że mamy dwie następujące asocjacje: $hash1{owoc} = jabłko; $hash1{kanapka} = hamburger; $hash1{picie} = szampan; $hash2{ciastko} = czekoladowe; $hash2{place} = jagodowy; $hash2{'lody w waflu’} = orzechowe; Możemy je ze sobą połączyć następująco: %bighash = (%hash1, %hash2); print $bighash{'lody w waflu’); orzechowe Użycie asocjacji i tablic w przypisaniach list W przypisaniach list można wykorzystać asocjacje i tablice. Po prawej stronie przypisania może wystąpić wiele asocjacji czy tablic, gdyż zostaną one i tak przekształcone na listy, jak w przykładzie z poprzedniego punktu: $hash1{owoc} = jabłko; $hash1{kanapka} = hamburger; $hash1{picie} = szampan; $hash2{ciastko} = czekoladowe; $hash2{place} = jagodowy; $hash2{‘lody w waflu’} = orzechowe; Powyższe dwie asocjacje połączymy ze sobą za pomocą przypisania list: %bighash = (%hash1, %hash2); print $bighash{'lody w waflu'}; orzechowe Trzeba uważać, przypisując elementy liście, która sama zawiera tablicę lub asocjację. W Perlu tablice i asocjacje automatycznie są powiększane podczas dodawania do nich elementów, wobec czego tablice i asocjacje powinny być używane po lewej stronie przypisania list tylko wtedy, gdy są ostatnimi elementami listy. W przeciwnym razie tablica lub asocjacja wchłonie wszystkie elementy, które miały być przypisane elementom następnej tablicy lub asocjacji z listy po lewej stronie operatora przypisania. Oto przykład przypisania do listy zawierającej dwa skalary i jedną tablicę uwagę, że tablica jest ostatnim elementem listy: ($variable1, $variable2, @array) = (l, 2, 3, 4, 5, 6, 7, 8); print "$variable1\n"; print "$variable2\n"; print "@array\n"; 1 2 3 4 5 6 7 8 Użycie typów ogólnych Typy ogólne w Perlu zachowują się jako aliasy. Oznacza to, że typu ogólnego można użyć do związania nazwy zmiennej, na przykład data, z nową nazwą zmiennej, na przykład alsodata. Powoduje to, że zmienne wszelkich typów, takie jak $alsodata, @alsodata czy %alsodata, odwołują się do tych samych danych, co zmienne $data, @data czy %data (to znaczy, że $alsodata ma takie samo znaczenie jak $data i dalej analogicznie). Oto przykład; najpierw tworzymy dwie zmienne, $data oraz @data: $data = „Tutaj są dane.”; @data = (1, 2, 3); Następnie tworzymy dla nazwy data alias alsodata: $data = "Tutaj są dane.' @data = (l, 2, 3) ; *alsodata = *data; Teraz $alsodata możemy używać jako synonimu $data: $data = "Tutaj są dane.' @data = (l, 2, 3) ; *alsodata = *data; print "$alsodata\n"; print @alsodata; Tutaj są dane. 123 Przypisanie typu ogólnego tak naprawdę kopiuje pełną nazwę w tablicy symboli pod nową nazwę (Perl przechowuje nazwy wszystkich typów danych związanych z nazwą, na przykład $data, %data i tak dalej, w tablicy symboli). Jeśli interesują Cię dalsze szczegóły tego procesu, zajrzyj do następnego tematu. Tak naprawdę symbol * w typie globalnym można uważać za pewnego rodzaju nazwę ogólną, oznaczającą dowolne typy danych ($, % i tak dalej). Kiedy używa się typów ogólnych, nie trzeba koniecznie kopiować całego symbolu dotyczącego danej nazwy. Jeśli użyje się przypisania tylko jednego typu danych, na przykład scalar, alias dotyczyć będzie tylko tego typu: $data = "Tutaj są dane."; @data = (l, 2, 3) ; *alsodata = \$data; # alias tylko części skalarnej W tym przypadku przypisaliśmy $data do $alsodata, ale nie przypisaliśmy już %data do %alsodata ani @data do @alsodata. Innymi słowy, o ile zadziała polecenie: print "$alsodata\n"; Tutaj są dane. to poniższe już nie zadziała: print @alsodata; Zagadnienie związane: Tworzenie wskaźnika Porównywanie typów ogólnych i haseł tablicy symboli Nazwy zmiennych Perl przechowuje w tablicy symboli, a każde hasło w tej tablicy jest typem ogólnym. Typy ogólne można uważać za asocjacje, których wartościami są wskaźniki na konkretne wartości zmiennych. Zapewne nie znajdziesz tego w dokumentacji Perla, ale klucze tych asocjacji, zapisane wielkimi literami, odpowiadają różnym możliwym typom danych, jak ARRAY, HASH i tak dalej. Można tę wiedzę wykorzystać do bezpośredniego wybierania danych z tablicy symboli. Jeśli na przykład mamy zmienną o wartości 5: $variable = 5; to *variable jest nazwą typu ogólnego zmiennej, zaś *variable (SCALAR) jest wskaźnikiem na wartość $variable. Za pomocą operatora dereferencji wskaźników Perla, $, można pobrać z tablicy symboli faktyczną wartość zmiennej $variable: $variable = 5; print ${*variable{SCALAR}}; 5 Zagadnienia związane Tworzenie wskaźnika Dereferencja wskaźnika Rozdział 4. Operatory i priorytety W skrócie W poprzednich trzech rozdziałach omawialiśmy standardowe postacie danych Perla. W tym rozdziale zaczniemy z danymi pracować przy użyciu operatorów. Operatory umożliwiają przetwarzanie danych, nawet jeśli są to operacje takie proste, jak dodawanie: print 2 + 2; 4 Można też wykonywać bardziej złożone obliczenia, na przykład za pomocą operatora trój argumentowego : while (<>) { print $_ < 10 ? $_ : "${\((a, b, c, d, e, f)[$_ - 10])}\n"; } Powyższe wyrażenie pobiera liczbę między l a 15 i zwraca odpowiadającą tej liczbie cyfrę szesnastkową ( więcej informacji o operatorze ? : znajdziesz dalej w tym rozdziale, w punkcie „Operator wyboru"). Operatory Perla należą do wielu różnych grup, ale z grubsza można je podzielić na operatory unarne, binarne, trójargumentowe i operatory list. Przykładem operatora unarnego jest operator negacji, ! ; te operatory mają jeden argument (na przykład $zaprzeczenie = !$ zmienna). Operatory binarne, takie jak dodawanie +, używają dwóch argumentów (na przykład $suma = 2 + 2 powoduje zapisanie w zmiennej $suma liczby 4). Przykładem operatora trójargumentowego jest ? : , a zgodnie z nazwą w tym przypadku mamy do czynienia z trzema argumentami (na przykład wyrażenie $modul = $zmienna >=0 ? $zmienna : -$zmienna powoduje przypisanie zmiennej $modul wartości bezwzględnej $zmiennej). Operatory list, choćby print, jako argumenty przyjmują listy (na przykład print l, 2, 3). Zaskakujące może być stwierdzenie, że, funkcja print została tu nazwana operatorem print. W Perlu użycie funkcji bez nawiasów uważa się za użycie operatora, wobec czego obowiązuje też priorytet operatorów (zajrzyj do pierwszego punktu w dziale „Zagadnienia" — w tym rozdziale). Priorytety są bardzo ważne w przypadku używania operatorów, więc nimi się teraz zajmiemy. Priorytety operatorów W tabeli 4. l zestawiono operatory Perla w kolejności malejących priorytetów. Tabela 4.1 Priorytety operatorów Operatory Łączność termy i lewostronne operatory list lewa -> lewa ++ -- nie dotyczy ** prawa ! ~ \ unarny + unarny - prawa =~ i~ lewa * / % x lewa + - . lewa << >> lewa operatory unarne z nazwami i operatory badania plików nie dotyczy < > <= >== lt gt le ge nie dotyczy == != <=> eq ne cmp nie dotyczy & lewa | ^ lewa && lewa || lewa .. ... nie dotyczy ?: prawa = += -= *= prawa , => lewa prawostronne operatory list nie dotyczy not prawa and lewa or xor lewa Priorytet operatorów Perla jest bardzo istotny. Często w jednym wyrażeniu używa się wielu operatorów, na przykład: print 2 + 3 * 4; Czy Perl najpierw doda 2 i 3, a dopiero wtedy wymnoży wynik przez 4? Czy może przemnoży 3 przez 4, a następnie doda 2? Na pytania te odpowiada tabela 4.l, która zawierająca priorytety operatorów. Mnożenie, *, ma wyższy priorytet niż dodawanie, +, wobec czego Perl przemnoży 3 przez 4 i następnie doda do wyniku 2: print 2 + 3 * 4; 14 Można oczywiście za pomocą nawiasów wymusić żądaną kolejność wykonania: print ((2 + 3) * 4) ; 20 Powyższy kod nie został zapisany prościej, jako print (2+3) * 4; gdyż print może działać albo jako operator, albo jako funkcja, a użycie nawiasów informuje Perla, że należy skorzystać z funkcji, zatem powyższe wywołanie zostałoby zinterpretowane tak, jakby miano wyświetlić 2 + 3. Wobec tego funkcja print zadziałałaby następująco: print (2+3) * 4; 5 Jest to dość zwodnicze zachowanie, trzeba się jednak do tego po prostu przyzwyczaić. Jeśli nie chce się, aby Perl zinterpretował operator listowy jako funkcję, można użyć przed nawiasami unarnego operatora +, który nie wpływa na wynik: print +(2 + 3) * 4; 20 Jak widać, priorytety operatorów są bardzo ważne. Cały układ tego rozdziału jest zgodny z układem tabeli 4. l: najpierw omawiane są operatory o priorytecie najwyższym. Zagadnienia: Najwyższy priorytet: termy i lewostronne operatory list Termy mają w Perlu najwyższy priorytet. Termami są zmienne, cytaty (wyrażenia w cudzysłowach lub z lewym ukośnikiem), wyrażenia w nawiasach, konstrukcje do i eval, tablice i asocjacje anonimowe tworzone za pomocą [ ] i {} (zajrzyj do rozdziału 8.) oraz funkcje, których parametry podaje się w nawiasach. Poza tym operatory list charakteryzują się bardzo silną łącznością lewostronną. Oznacza to, że przetwarzanie w przypadku operatorów list odbywa się od lewej strony. Wobec tego operator list sort pobiera kolejne wartości za nim się znajdujące (wcześniej tylko operator „przecinek" łączy wartości w listę), a później całą resztę wyrażenia traktuje jako pojedynczy term: print l, 2, 3, 4, sort, 9, 8, 7, 6, 5; 123456789 Zagadnienie związane: Tworzenie wskaźników na tablice anonimowe Tworzenie wskaźników na asocjacje anonimowe Operator „strzałka" Operator „strzałka", ->, jest infiksowym operatorem dereferencji wzorowanym na języku C. Jeśli używamy go, podając [ ] lub {} po jego prawej stronie, po lewej stronie tego operatora musi się znaleźć wskaźnik do tablicy lub asocjacji. Oto przykład, w którym tworzymy wskaźnik na asocjację, stosując operator \, a następnie używamy operatora -> do odwołania się do wartości elementu tej asocjacji: $hash{owoc} = jabłko; $hash{kanapka} = hamburger; $hash{picie} = szampan; $hashref = \%hash; print $hashref->{kanapka}; hamburger Jeśli po prawej stronie nie użyto [ ] ani {}, zaś lewa strona nie jest wskaźnikiem na tablicę czy asocjację, lewa strona operatora „strzałka" musi być nazwą obiektu lub klasy, zaś prawa strona musi być metodą: $result = $myobject->mymethod($data) ; Zagadnienie związane: Tworzenie wskaźnika Tworzenie klasy Inkrementacja i dekrementacja Operatory inkrementacji ++ oraz dekrementacji -- działają tak samo, jak w języku C. Jeśli występują przed zmienną, wartość tej zmiennej zwiększają bądź zmniejszają przed zwróceniem jej wartości. Jeśli występują za zmienną, najpierw zwracają jej wartość, a dopiero potem ją modyfikują. W sytuacji, gdy na przykład mamy zmienne $variable1 oraz $variable2: $variable1 = 1; $variable2 = 1; można za pomocą przedrostkowego operatora + + zwiększyć wartość zmiennej $variablel: print ++$variable1 . "\n"; 2 Z drugiej strony na zmiennej $variable2 można użyć operatora przyrostkowego ++, aby jej wartość zwiększyć dopiero po jej zwróceniu: print $variable2++ . "\n"; print $variable2 . "\n"; 1 2 Warto zauważyć, że operator ++ działa także na skalarnych zmiennych zawierających tekst (o ile oczywiście zmienne te nie zostaną umieszczone w kontekście numerycznym). Poniższy kod: $variable = 'AAA’; print ++$variable . "\n"; $variable = 'bbb'; print ++$variable . "\n"; $variable = 'zzz'; print ++$variable . "\n"; da następujący wynik: AAB bbc aaaa Potęgowanie Operatorem potęgowania jest **. Jest to operator binarny, który podnosi pierwszą liczbę do potęgi określonej drugim argumentem, na przykład: print 2 ** 10; 1024 Symboliczne operatory unarne Istnieją cztery symboliczne operatory unarne: ! — logiczne przeczenie (not), - — negacja arytmetyczna, ~ — negacj a bitowa (uzupełnienie j edynki), \ — tworzy wskaźnik do znajdującej się za tym wartości. Jako przykład podajmy, że negacją logiczną zera jest jedynka: print !0; 1 Negacja bitowa zmienia po prostu wszystkie bity liczby. Można użyć jej na przykład do sprawdzenia, jaka jest największa możliwa liczba całkowita bez znaku na danym komputerze — wystarczy użyć wyrażenia ~0, a w wyniku otrzyma się ciąg bitów o wartości l: print ~0; 4294967295 Zatem wynikiem jest 4 294 967 295, czyli 2 ** 32 - 1. Operatory wiązania Operator wiązania, =~, wiąże wyrażenie skalarne z dopasowaniem wzorca. Operacje tekstowe —jak s///, m// czy tr// — działają domyślnie na $_. Operatora =~ można użyć do sprawienia, żeby operacje te zadziałały na wskazanej zmiennej skalarnej. Na przykład przypiszmy tekst zmiennej $line: $line = ".Hello!”; Teraz możemy użyć operacji dopasowania m// na tej zmiennej w następujący sposób: $line = ".Hello!"; if ($lina =~ m/*\./) { print "Zdania nie zaczyna się od kropki!"; } Zdania nie zaczyna się od kropki! Operator ! ~ działa jak =~, tyle tylko, że zwracana wartość jest zanegowana. Zagadnienie związane: Wyrażenia regularne Mnożenie i dzielenie Operator mnożenia, *, mnoży dwie liczby: print 2 * 4; 8 Operator dzielenia, /, dzieli dwie liczby: print 16 / 3; 5.33333333333333 Operator modulo, %, zwraca resztę z dzielenia dwóch liczb przez siebie: print 16 % 3; 1 Operator powtórzenia, x, pozwala powtórzyć działanie. W kontekście skalarnym x zwraca ciąg znaków, składający się z lewego argumentu powtórzonego tyle razy, ile wskazuje prawy argument. W kontekście list powtarzana jest lista, jeśli lewy argument jest listą ujętą w nawiasy. Jako przykład wyświetlimy wyraz składający się z trzydziestu kresek: print '-' x 30; ------------------------------ Dodawanie, odejmowanie i konkatenacja Operator dodawania (czyli +) zwraca sumę dwóch liczb: print 2 + 2; 4 Binarny operator (czyli -) odejmuje od siebie dwie liczby, zwraca ich różnicę: print 4-2; 2 Binarny operator (czyli .) łączy dwa ciągi znaków, na przykład: print "Hello " . "there."; Hello there. Operatory przesunięć Operator przesunięcia w lewo, <<, zwraca swój lewy argument przesunięty w lewo o liczbę miejsc określoną argumentem prawym, na przykład: print 2 << 10; 2048 Z kolei operator przesunięcia w prawo, >>, zwraca swój lewy argument przesunięty w prawo o liczbę miejsc określoną argumentem lewym, na przykład: print 2048 >> 3; 256 Nazwane operatory unarne Funkcje Perla, takie jak sqrt, define, eval, return, chdir, rmdir, oct, hex, undef, exists i inne, przyjmujące jeden argument skalarny (nie listę), są nazwanymi operatorami unarnymi, jeśli tylko ich argumenty nie zostaną ujęte w nawias. Oto przykład użycia operatora pierwiastkowania (drugiego stopnia), sqrt: print sqrt 4; 2 Użycie operatorów testowania plików Perl obsługuje szereg operatorów testowania plików, które pozwalaj ą uzyskać mnóstwo informacji o plikach i ich uchwytach (więcej wiadomości o obsłudze plików w Perlu znajdziesz w rozdziale 12.). Oto składnia tych operatorów, przy czym X oznacza konkretny operator: -X UCHWYTPLIKU -X WYRAŻENIE -X Możliwe wartości -x zestawiono w tabeli 4.2. Jeśli pominięty zostanie argument, operacja dotyczy zawartości zmiennej $_ (poza operatorem -t, który bada domyślnie STDIN). Dla osób, dla których system Unix jest zagadką, taką samą zagadką będzie zapewne znaczna część informacji z tabeli 4.2. Na przykład UID i GID odnoszą się do identyfikatorów użytkownika i grupy w systemie Unix. Oto kilka przykładów zastosowania powyższych operatorów do uchwytu STDIN (pamiętaj, że operatory te zwracają l jako prawdę): print -e STDIN; # Czy STDIN istnieje? l print -t STDIN; # Czy STDIN powiązany jest z terminalem? l print -z STDIN; # Czy STDIN ma zerowy rozmiar? l Zagadnienie związane: stat — status pliku Tabela 4.2. Operatory testowania plików Operator Zwracana informacja o pliku -A Czas od ostatniego dostępu do pliku. -B Czy plik jest binarny. -b Czy plik jest specjalnym plikiem blokowym. -c Czy plik jest specjalnym plikiem tekstowym. -C Czas od ostatniej zmiany i-węzła. -d Czy plik jest katalogiem. -e Czy plik istnieje. -f Czy plik jest zwykłym plikiem. -g Czy ustawiono bit SETGID. - k Czy ustawiono bit łączenia. -l Czy plik jest „linkiem" symbolicznym. -M Wiek pliku w dniach — w chwili uruchomienia skryptu. -o Obowiązujący UID właściciela. -O Rzeczywisty UID właściciela. -P Czy plik jest nazwanym potokiem. - r Czy bieżący UID/GID pozwala odczytać plik. -R Czy rzeczywisty UID/GID pozwala odczytać plik. - s Czy rozmiar pliku nie jest zerem (operator ten faktycznie zwraca rozmiar pliku). -S Czy plik jest gniazdem (socket). -t Czy uchwyt pliku związano z terminalem. -T Czy plik jest plikiem tekstowym. -u Czy ustawiono bit SETUID. -w Czy bieżący UID/GID umożliwia zapisywanie do pliku. -W Czy rzeczywisty UID/GID umożliwia zapisywanie do pliku. -x Czy bieżący UID/GID umożliwia wykonywanie pliku. -X Czy rzeczywisty UID/GID umożliwia wykonywanie pliku. -z Czy plik ma zerowy rozmiar. Operatory porównania Operatory porównania to operatory binarne realizujące porównania, zwracające 1 jako prawdę i ciąg pusty jako fałsz. Operatory porównania, jak na przykład większość czy mniejszość-równość, zestawiono w tabeli 4.3. Warto zaznaczyć, że istnieje osobna grupa operatorów numerycznych i oddzielna grupa operatorów do porównywania tekstu. Tabela 4.3. Operatory porównania Operator Typ danych Zwraca < liczbowe Prawda, jeśli lewy argument jest mniejszy od prawego. > liczbowe Prawda, jeśli lewy argument jest większy od prawego. <= liczbowe Prawda, jeśli lewy argument jest mniejszy od prawego lub mu równy. >= liczbowe Prawda, jeśli lewy argument jest większy od prawego lub mu równy. lt tekstowe Prawda, jeśli lewy argument jest mniejszy od prawego. gt tekstowe Prawda, jeśli lewy argument jest większy od prawego. le tekstowe Prawda, jeśli lewy argument jest mniejszy od prawego lub mu równy. ge tekstowe Prawda, jeśli lewy argument jest większy od prawego lub mu równy. Pamiętaj, że operator większy bądź równy zapisuje się jako >=, niejako => (ten ostatni to synonim operatora „przecinek"). Oto przykład sprawdzania danych liczbowych podawanych przez użytkownika. Jeśli wartość jest większa od 100, wyświetla się komunikat o błędzie: while (<>) { if ($_ > 100) { print "Zbyt wiele!\n"; } } Do łączenia ze sobą warunków można używać operatorów logicznych && i | | lub ich odpowiedników o niskim priorytecie, and i or. W poniższym przykładzie żąda się, żeby dane podawane przez użytkownika mieściły się między literami k i m: print "Proszę wprowadzić literę z zakresu k..m\n"; while (o) { chop; if ($_ lt 'k’ or $_ gt 'm') { print "Proszę wprowadzić literę między k a m\n"; } else ( print "Dziękuję, teraz następna!\n"; } Użycie operatorów równości Oprócz operatorów porównania, omówionych w poprzednim punkcie, Perl obsługuje także operatory równości z tabeli 4.4. Podobnie jak w przypadku operatorów porównania, tak i tym razem mamy do czynienia z osobnymi zestawami do danych liczbowych i danych tekstowych. Tabela 4.4. Operatory równości Operator Typ danych Zwraca == liczbowe Prawdę, jeśli lewy argument jest równy argumentowi prawemu. != liczbowe Prawdę, jeśli lewy argument nie jest równy argumentowi prawemu. <=> liczbowe -l, 0 lub l odpowiednio, kiedy lewy argument jest mniejszy od prawego, kiedy jest mu równy lub kiedy jest większy od prawego argumentu. eq tekstowe Prawdę, jeśli lewy argument jest równy argumentowi prawemu. ne tekstowe Prawdę, jeśli lewy argument nie jest równy argumentowi prawemu. cmp tekstowe -l, 0 lub l odpowiednio, kiedy lewy argument jest mniejszy od prawego, kiedy jest mu równy lub kiedy jest większy od prawego argumentu. Oto przykład, w którym prosimy użytkownika o wciśnięcie litery „y". Póki użytkownik tego nie zrobi, stale będzie wyświetlany komunikat o błędzie: print "Wciśnij literę y\n"; while {<>) { chop; if ($_ ne 'y') { print "Wciśnij literę y\n"; } else { print "Czy zawsze jesteś taki posłuszny/a?\n" ; exit; } } Zobaczmy, jak może wyglądać przykładowa sesja: Wciśnij literę y a Wciśnij literę y b Wciśnij literę y y Czy zawsze jesteś taki posłuszny/a? Iloczyn bitowy Operator & zwraca iloczyn bitowy swoich argumentów. Każdy bit pierwszego argumentu jest mnożony bitowo przez odpowiadający mu bit prawego argumentu, zgodnie z rysunkiem 4. l. Rysunek 4.1. Bitowe mnożenie and 0 1 0 0 0 1 0 1 Na przykład bitowe mnożenie 5 (ustawione bity 0 i 2) przez 4 (ustawiony bit 4) daje w rezultacie 4: print 5 & 4 ; 4 Bitowa alternatywa Operator | zwraca bitową alternatywę swoich argumentów, według rysunku 4.2. Rysunek 4.2. Bitowa alternatywa or 0 1 0 0 1 1 1 1 Na przykład bitowa alternatywa 4 (ustawiony bit 2) i l (stawiony bit 0) daje w wyniku 5 (ustawione bity 0 i 2): print 4 | 1; 5 Bitowa alternatywa wyłączająca Operator alternatywy wyłączającej (XOR), oznaczany symbolem ", zwraca swoje argumenty poddane działaniu takiej alternatywy — bit po bicie, zgodnie z rysunkiem 4.3. Rysunek 4.3. Bitowa alternatywa wyłączająca xor 0 1 0 0 1 1 1 0 Alternatywa wyłączająca zachowuje się tak, jak zwykła alternatywa, z wyjątkiem sytuacji, kiedy natrafiają na siebie dwie jedynki, gdyż wtedy alternatywa wyłączająca daje w wyniku zero. Oto kilka przykładów: print 0 ^ 0; 0 print l ^ 0; 1 print 5 ^ 4; 1 Użycie logicznej koniunkcji w stylu C Wzorowany na języku C — operator && realizuje logiczną koniunkcję, umożliwiając w ten sposób łączenie kolejnych fraz z operatorami porównania w całość. Aby całe takie wyrażenie było prawdziwe, muszą być prawdziwe również wszystkie jego wyrażenia składowe. Prześledźmy przykład: print "Proszę podać liczbę między 5 a 10\n"; while (<>) { chop; if ($_ >= 5 && $_ <= 10) { print "Dziękuję - teraz następna!\n"; } else { print 'Proszę podać liczbę między 5 a 10\n" } } Operator ten jest uważany za operator wzorowany na języku C, gdyż używa się takiego samego symbolu, jak w języku C, taki sam jest też jego priorytet (w Perlu istnieje jeszcze jeden operator koniunkcji logicznej, and, który ma niższy priorytet). Operator ten jest nazywany również zworką —jeśli jego lewa strona jest fałszywa, prawa strona nawet nie jest wyliczana. Operator Perla różni się od operatora && z języka C o tyle, że zamiast zwracać 0 lub l, zwraca ostatnią wyliczoną wartość. Użycie logicznej alternatywy w stylu C Wzorowany na języku C — operator || realizuje logiczną koniunkcję, umożliwiając w ten sposób łączenie kolejnych fraz z operatorami porównania w całość. Aby całe takie wyrażenie było prawdziwe, wystarczy, że prawdziwe będzie dowolne jego wyrażenie składowe. Oto przykład, w którym wyświetlamy komunikat o błędzie, jeśli tylko użytkownik poda cyfrę spoza podanego zakresu: print "Proszę podać liczbę między 5 a 10\n"; while (<>) { chop; if ($_ < 5 || $_ > 10) { print "Proszę podać liczbę między 5 a 10\n"; } else { print "Dziękuję — teraz następna!\n"; } } Operator ten jest nazywany także zworką —jeśli jego lewa strona jest prawdziwa, prawa strona nawet nie jest wyliczana. Operator Perla różni się od operatora | | z języka C o tyle, że zamiast zwracać 0 lub l, zwraca ostatnią wyliczoną wartość. Operator || jest przydatny do skracania wyliczeń, gdyż zwraca wartość, kiedy tylko pierwszy jego argument będzie prawdą (a w Perlu często to właśnie wystarcza). W ten sposób można wypróbować różne metody zrealizowania jakiegoś zadania przed poddaniem się, jak w poniższym przykładzie: $result = to($data) || that ($data) || die "Nie działa ani to, ani tamto"; W tym przypadku ostatecznie „poddajemy się" funkcji die, która kończy działanie programu, wyświetlając komunikat „Nie działa ani to, ani tamto at try.pl line X". Końcówka — „at try.pl lineX" — wskazuje kolejno plik i wiersz, w których nastąpiło przerwanie programu. Operator ten uważa się za operator wzorowany na języku C, gdyż używany jest taki sam symbol, jak w języku C, taki sam jest też jego priorytet (w Perlu istnieje jeszcze jeden operator alternatywy logicznej, or, który ma niższy priorytet). Operator zakresu Operator zakresu (czyli ..) może działać na dwa sposoby, zależnie od kontekstu. W kontekście list zwraca tablicę wartości z zakresu określonego jego argumentami. Oto przykład pięciokrotnego wyświetlenia danego tekstu: for (l .. 5) { print "I jeszcze raz!\n" } I jeszcze raz I jeszcze raz I jeszcze raz I jeszcze raz I jeszcze raz Użyty w kontekście skalarnym operator .. zwraca wartość logiczną, która jest fałszem, jeśli fałszem jest lewy operand. Jeżeli lewy argument jest prawdą, zwracana jest prawda, dopóki prawdą jest prawy argument, potem wynikiem znów staje się fałsz. Jeśli operator nie ma sprawdzać wartości prawego argumentu aż do następnej iteracji, należy użyć zamiast dwóch — trzech kropek (...). W takim przypadku prawy operand nie jest wyliczany, jeśli wynikiem operatora jest fałsz, zaś lewy argument nie jest wyliczany, póki wartością operatora jest prawda. W kontekście skalarnym wartość zwracana przez operator zakresu jest ciągiem pustym (fałsz) lub kolejnym numerem (prawda). Do zakresu możliwych kolejnych numerów dodano „E0". Wyraz ten, powodujący wymnożenie ostatniego numeru przez 10, podniesiony do potęgi 0 (czyli przez 1), nie wpływa na wartość numeru. Jeśli konieczne jest odnotowanie zakończenia sekwencji, należy po prostu wyszukać tekst „E0". Jeśli którykolwiek argument operatora zakresu w kontekście skalarnym jest stałą, argument ten jest automatycznie porównywany z wbudowaną zmienną Perla $., która zawiera bieżący numer wiersza. Operator wyboru Operator wyboru ?: ma trzy argumenty i działa bardzo podobnie do konstrukcji if-then-else. Jeśli argument przed znakiem ? jest prawdą, wyliczany i zwracany jest argument sprzed dwukropka; w przeciwnym razie wyliczany i zwracany jest argument zza dwukropka. Oto przykład pokazujący sposób wyliczenia wartości bezwzględnej liczby podanej przez użytkownika (przez chwilę założymy, że nic nam nie wiadomo o wbudowanej funkcji abs określającej wartość bezwzględną): while (<>) { print $_ >= 0 ? $_ : -$_ } W tej sytuacji wpisane liczby są porównywane z zerem. Jeśli liczba jest większa lub równa zero, wówczas jest wyświetlana. W przeciwnym razie jest używany unarny operator — zmieniający znak liczby. Oto kolejny przykład, w którym podane przez użytkownika liczby zamieniamy na szesnastkowe odpowiedniki: while (<>) { print $_ < 10 ? $_ : "${\((a, b, c, d, e, f)[$_ - 10])}\n"; } Zauważ, że brakuje tu procedury obsługi błędów. Zagnieżdżony operator ?: pozwoli nam zachować dalej idącą ostrożność i sprawdzić wartość wejściową oraz wyświetlić komunikat, jeśli podanej liczbie nie odpowiada pojedyncza cyfra szesnastkowa: while(<>) { print $_ > 0 && $_ < 10 ? $_ : "${\($_ < 16 ? (a, b, c, d, e, f)[$_ - 10] : \"Podanej liczbie nie odpowiada pojedyncza cyfra szesnastkowa.\”)}\n"; } Przypisania Operator przypisania = przypisuje dane wartościom lvalue, na przykład zmiennym: $variable1 = 5; Można też, jak w języku C, używać skróconych operatorów przypisania. Składają się one z przypisania połączonego z innym operatorem, jak w poniższym przykładzie: $doubleme *= 2; Takie przypisanie wymnoży wartość $doubleme przez 2 i wynik zapisze w tej samej zmiennej. Poniżej przedstawiamy skrócone operatory przypisania: **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x= W przeciwieństwie do języka C, operator przypisania daje poprawną wartość lvalue — tak jak poniżej, gdzie odrzucamy końcówkę wartości $input (nie zaś całą wartość zwracaną przez operację przypisania): chop ($input = 123); print $input; 12 Ta cecha jest wygodna, jeśli chodzi o skracanie kodu, gdyż w jednym wierszu można odczytać dane wejściowe, obciąć je i wynik zapisać w zmiennej $input: chop ($input = <>) ; Przecinek jako operator Operator, działa inaczej w kontekście skalarnym, a inaczej w listowym. W kontekście skalarnym wylicza lewy argument, odrzuca jego wartość, wylicza prawy argument i zwraca jego wartość. Oto przykład: $variable = (l, 2, 3); print $variable; 3 W kontekście listowym przecinek służy jako separator wartości listy, wstawia na listę oba swoje argumenty: $array = (l, 2, 3); print join(", ", @array); l, 2, 3 Zwróć uwagę na to, że symbol => (nazywany dwuznakiem =>) jest synonimem przecinka jako operatora. Od wersji 5.001 Perla operator => wymusza traktowanie dowolnego słowa po swojej lewej stronie jako tekst. Użycie prawostronnych operatorów list Prawa strona operatorów listy ma niski priorytet, wobec czego operator przecinek (patrz: poprzedni punkt) pozwala utworzyć listę przed jej przekazaniem operatorowi list. Oto przykład, który był już prezentowany wcześniej w tym rozdziale: print l, 2, 3, 4, sort 9, 8, 7, 6, 5; 123456789 Logiczna negacja Operator not zwraca logiczne zaprzeczenie swojego argumentu. Operator ten działa tak samo, jak !, ale ma bardzo niski priorytet (oznacza to, że można zapomnieć o nawiasach obejmujących wyrażenia). Zobaczmy przykład: print not 0; 1 Logiczna koniunkcja Operator and działa tak samo, jak &&, ale ma bardzo niski priorytet. Operator ten najpierw wylicza swój lewy argument, zaś argument prawy jest wyliczany tylko wtedy, gdy lewy jest prawdą. Operator and pozwala zatem skracać wyliczenia tak samo, jak &&. Logiczna alternatywa Operator o r zwraca wartość pierwszego argumentu będącego prawdą (od lewej strony). Działa tak samo, jak ||, ale ma bardzo niski priorytet. Operator ten najpierw wylicza swój lewy argument, zaś argument prawy jest wyliczany tylko wtedy, gdy lewy jest fałszem. Dzięki jego niskiemu priorytetowi można zrezygnować z dodatkowych nawiasów w przypadku korzystania z operatorów list. Z uwagi na to, że zwracana jest wartość pierwszego argumentu, mającego wartość prawdy (czyli w Perlu dowolnego argumentu niebędącego zerem), operator ten często jest stosowany do skracania wyliczeń — jak w poniższym przypadku, gdzie próbujemy otworzyć plik (jeśli się to nie uda, wyświetli się komunikat o błędzie i działanie programu zakończy funkcja die): open FileHandle, $filename or die "Nie można otworzyć $filename\n"; Logiczna alternatywa wykluczająca Operator xor zwraca alternatywę wykluczającą swoich argumentów. Działa jak ^, z tym zastrzeżeniem, że ma niski priorytet. Rozdział 5. Instrukcje warunkowe i pętle W skrócie W tym rozdziale nauczymy się sterować pracą programu za pomocą instrukcji warunkowych oraz pętli. Poznamy też pewne dodatkowe instrukcje sterujące działaniem programu, takie jak goto, exit i die. Instrukcje warunkowe Instrukcje warunkowe — zwane rozgałęzieniami — umożliwiają zakodowanie sposobu działania programu w zależności od spełnienia pewnych warunków. Instrukcje warunkowe pozwalają w kodzie podejmować decyzje i stosownie do nich się zachowywać. W poniższym przykładzie użyjemy instrukcji i f do sprawdzenia wartości zmiennej $variable. Jeśli jest to 5, wyświetlimy wyraz „Tak, to pięć.", w przeciwnym razie pojawi się wyraz „Nie, to nie jest piątka.": $variable = 5; if ($variable == 5) { print "Tak, to pięć.\n"; } else { print "Nie, to nie jest piątka.\n"; } Tak, to pięć. Nawet w takim prostym przykładzie widać, jak potężną instrukcją jest if: sprawdza spełnienie wyrażenia logicznego podanego w nawiasach i jeśli okaże się ono prawdziwe (czyli różne od zera), wykonuje pierwszy blok kodu. W przeciwnej sytuacji wykonywany jest (opcjonalny) blok else. Instrukcja if jest instrukcją złożoną, co oznacza, że używa się w niej nawiasów klamrowych obejmujących blok lub bloki kodu. Dzięki temu, że Perl pomija białe znaki, w tym znaki nowego wiersza, powyższy kod można zapisać następująco: $variable = 5; if ($variable == 5) { print "Tak, to pięć.\n"; } else { print "Nie, to nie jest piątka.\n"; } Nie można natomiast użyć znanej z języka C składni, która pozwala pominąć nawiasy klamrowe, jeśli blok składa się z pojedynczej instrukcji: $variable = 5; if ($variable == 5) # źle! print "Tak, to pięć.\n"; else print "Nie, to nie jest piątka.\n"; Instrukcje warunkowe, takie jak if, decydują o przepływie sterowania w programie, a na tym właśnie polega programowanie: na podejmowaniu decyzji. Pętle Równie silnym narzędziem programistycznym są pętle, gdyż pozwalają one wielokrotnie wykonywać operacje na zbiorach danych, a w tym właśnie komputery są niedoścignione: w szybkim robieniu powtarzalnych wyliczeń. Pętle powodują stałe wykonywanie fragmentu kodu tak długo, aż spełniony zostanie wskazany warunek. W tej książce już nieraz używaliśmy pętli while. Oto przykład wczytujący dane ze STDIN i wyświetlający poszczególne wiersze: while (<>) { print; } W bardziej złożonych pętlach można używać indeksu pętli, na przykład w poniższej pętli for wyliczającej wartość silni: $factorial = 1; for ($loop_index = 1; $loop_index <= 6; $loop_index++) { $factorial *= $loop_index; } print "6! = $factorial\n" ; 6! = 720 Dzięki zastosowaniu indeksu pętli można ponumerować wartości ze zbioru danych, przetwarzając dane kolejno, jak w poniższym przykładzie, gdzie tworzymy iterację po elementach tablicy: $array = ("raz", "dwa", "trzy"); for ($loop_index = 0; $loop_index <= $#array; $loop_index++) { print $array[$loop_index] . " "; } raz dwa trzy I o to właśnie chodzi. Instrukcje warunkowe pozwalają podejmować decyzje, zaś pętle umożliwiaj ą powtarzanie tych samych operacji na kolejnych danych. Obie grupy instrukcji są bardzo ważnymi narzędziami, jakie programista ma do swojej dyspozycji, czas więc nauczyć sieje stosować. Zagadnienia: Instrukcja if Instrukcja i f jest najważniejszą instrukcją warunkową w Perlu. Instrukcja ta sprawdza wyrażenie podane w nawiasach i jeśli jest ono prawdziwe (czyli różne od zera), wykonywany jest odpowiedni blok kodu. Można użyć frazy else do podania kodu, który ma być uruchomiony, jeśli warunek jest fałszywy, jak również można zastosować elsif (uwaga na pisownię — nie jest to else if ani elseif) do sprawdzania kolejnych warunków. Oto ogólna składnia instrukcji i f: if (WYRAŻENIĘ) BLOK if (WYRAŻENIE) BLOK else BLOK if (WYRAŻENIE) BLOK elsif (WYRAŻENIE) BLOK ... else BLOK Poniżej przedstawiamy przykład, w którym używamy operatora równości == do sprawdzenia, czy zmienna ma wartość 5, a jeśli tak, informujemy o tym użytkownika: $variable = 5; if ($variable == 5) { print "Tak, to pięć.\n"; } Tak, to pięć. Można podać też kod frazy else, który będzie uruchamiany, jeśli wyrażenie warunkowe instrukcji i f okaże się fałszywe: $variable = 6; if ($variable == 5) { print "Tak, to pięć.\n"; } else { print "Nie, to nie pięć.\n"; } Nie, to nie pięć. Istnieje też możliwość dodania frazy elsif realizującej dowolną liczbę dalszych sprawdzeń. W poniższym przykładzie, jeśli pierwszy warunek jest prawdziwy, sprawdzany jest warunek drugi. Jeżeli drugi jest też fałszywy, sprawdzany jest warunek trzeci — i tak dalej. W sytuacji, gdy żaden warunek nie jest spełniony, wykonywany jest kod frazy else (zajrzyj też do punktu „Instrukcja switch", dalej w tym rozdziale): $variable = 2; if ($variable == 1) { print "Tak, to jeden.\n"; }elsif ($variable — 2) { print "Tak, to dwa.\n"; }elsif ($variable == 3) { print "Tak, to trzy.\n"; }elsif ($variable == 4) { print "Tak, to cztery.\n"; }elsif ($variable == 5) { print "Tak, to pięć.\n"; } else { print "Niestety, brak takiej wartości!\n"; } Tak, to dwa. Instrukcja unless Instrukcja unless jest w pewnym sensie odwrotnością instrukcji if — działa tak samo jak if, ale związany z nią blok kodu jest wywoływany, jeśli podany warunek nie jest spełniony. Oto obowiązująca składnia: unless (WYRAŻENIE) BLOK unless (WYRAŻENIE) BLOK else BLOK unless (WYRAŻENIE) BLOK elsif (WYRAŻENIE) BLOK ... else BLOK W podanym przykładzie używamy znanej już pętli while. Kod ten wypisuje dane podane przez użytkownika, chyba że dane te będą się zaczynać od litery q lub Q. Dane wprowadzane przez użytkownika można sprawdzać, stosując wzorce wyrażeń (więcej na ten temat w rozdziale 6.). Jeśli użytkownik wpisze jedną z wybranych liter, program kończy swoje działanie: while (<>) { chomp; unless (/^q/i) { print; } else { exit; } } Zagadnienie związane: Wyrażenia regularne Pętla for Pętli for używa się do realizacji pętli, zwykle z wykorzystaniem indeksu pętli. Oto ogólna składnia instrukcji for: ETYKIETA for (WYRAŻENIE; WYRAŻENIE; WYRAŻENIE) BLOK Pierwsze wyrażenie jest wykonywane przed wykonaniem BLOKU. Drugie wyrażenie jest sprawdzane w każdej iteracji i jeśli okaże się fałszywe, wykonywanie pętli jest przerywane. Trzecie wyrażenie jest wykonywane po każdej iteracji. Jeśli warunek okaże się fałszywy już na początku, kod zawarty w bloku może nie zostać wykonany ani razu. Pętli tej można używać na różne sposoby, przy czym sposób najbardziej typowy pokazano poniżej — za pomocą zmiennej kontrolnej $loop_index wyświetlamy pięciokrotnie wyraz „Hello!\n": for ($loop_index = 1; $loop_index <= 5; $loop_index++) { print "Hello !\n"; } Hello! Hello! Hello! Hello! Hello! Można użyć też więcej niż jednego indeksu pętli: for ($loop_index = 0, $double = 0; $loop_index <= 4 ; $loop_index++ , $double = 2 * $loop_index) { print "Podwojony indeks pętli " . $loop_index . " ma wartość " . $double . "\n"; } Podwojony indeks pętli 0 ma wartość 0. Podwojony indeks pętli l ma wartość 2. Podwojony indeks pętli 2 ma wartość 4. Podwojony indeks pętli 3 ma wartość 6. Podwojony indeks pętli 4 ma wartość 8. Nawet po zakończeniu działania pętli można użyć jej indeksu, na przykład w celu sprawdzenia, ile iteracji się wykonało: $factorial = 1; for ($loop_index = 1; $loop_index <= 6; $loop_index++) { $factorial *= $loop_index; } print $loop_index - l . " ! = $factorial\n" ; 6! = 720 Stosowanie pokazanej wyżej konstrukcji nie jest jednak zalecane, gdyż nie ma gwarancji, że w przyszłych wersjach Perla będzie ona nadal działała. Jeśli zmienne pętli mają być poza nią niedostępne, można użyć deklaracji my: $factorial = 1; for (my $loop_index = 1; $loop_index <= 6; $loop_index++) { $factorial *= $loop_index; } W pętli for tak naprawdę nawet nie trzeba używać indeksów. Oto przykład, w którym wczytujemy wiersze tekstu ze STDIN i wyświetlamy je, aż użytkownik wpisze coś zaczynającego się od q lub Q. Zauważ, że dane wejściowe zapisujemy w zmiennej $line, gdyż dane z o są zmiennej $_ przypisywane domyślnie tylko w pętli while: for ($line = 0; $line !~ /^q/i; $line = 0) { print $line; } W Perlu pętle for i foreach to tak naprawdę ta sama pętla. Zobaczmy przykład zastosowania foreach zamiast for — z użyciem indeksu pętli: foreach ($loop_index = 1; $loop_index <= 5; $loop_index++) { print "Hello !\n"; } Hello! Hello! Hello! Hello! Hello! Analogicznie, pętli for można użyć zamiast foreach (więcej o tej ostatniej w następnym punkcie): @array = "Hello ", "there. \n") ; for (@array) {print;} Hello there. Zagadnienie związane: Wyrażenia regularne Użycie my do określania zakresu Pętla foreach Instrukcja foreach jest w Perlu tą samą instrukcją, co for — są to synonimy. Mimo to programiści do przetwarzania listy (dla każdego elementu spośród elementów następujących) częściej używaj ą właśni e foreach. Oto typowy sposób jej zastosowania: ETYKIETA foreach ZMIENNA (LISTA) BLOK Pętla ta przetwarza listę, przypisując kolejne jej wartości zmiennej ZMIENNA oraz każdorazowo wykonując BLOK. Oto przykład, w którym przetwarzamy w pętli elementy tablicy, umieszczając je w zmiennej $element: @array = (l, 2, 3); foreach $element (@array) { $element +=1; } print join(", ", @array); 2, 3, 4 Jeśli nazwa zmiennej nie będzie podana, zastosowana zostanie zmienna $_, co może być wygodne w przypadku używania funkcji, które domyślnie korzystają z tejże zmiennej, jak na przykład print. Oto sposób, w jaki możemy wyświetlić elementy tablicy właśnie przy użyciu zmiennej $_: @array = ("Hello ", "there.\n"); foreach (@array) {print;} Hello there. Stosując jedną z funkcji keys lub values, można także przetwarzać asocjacje: $hash{owoc} = 'pomarańcza’; $hash{kanapka} = klubowa; $hash{picie} = lemoniada; foreach $key (keys %hash) { print $hash{$key) . "\n"; > } lemoniada klubowa pomarańcza Za każdym razem, kiedy już lista zostanie przekazana do foreach, nie należy modyfikować listy wewnątrz pętli (na przykład wydzielając z niej warstwy), gdyż może to spowodować załamanie się foreach. Funkcję each przygotowano do pracy w sposób bardzo podobny do instrukcji foreach. Funkcja ta zwraca kolejne elementy asocjacji, jak w poniższym przykładzie: $hash{owoc} = 'pomarańcza1; $hash{kanapka} = klubowa; $hash{picie} = lemoniada; while(($key, $value) = each(%hash)) { print "$key => $value\n"; } picie => lemoniada kanapka => klubowa owoc => pomarańcza Zagadnienie związane: Przetwarzanie asocjacji w pętli Pętla while Pętla while jest w Perlu bardzo ważna. Oto sposób jej użycia: ETYKIETA while (WYRAŻENIE) BLOK ETYKIETA while (WYRAŻENIE) BLOK continue BLOK Pętla ta wykonuje BLOK tak długo, jak długo WYRAŻENIE jest prawdziwe. Warto zaznaczyć, że pętla while jest prosta w użyciu. W poniższym przykładzie dodajemy oszczędności użytkowników, aż osiągniemy milion: $savings = 0; while ($savings < 1_000_000) { print "Podaj zarobioną dzisiaj kwotę: "; $savings += <>; } print "Gratuluję, milionerze . \n" ; Oto kolejny przykład, w którym użyto pętli while do przeglądania par klucz+wartość, zwracanych przez funkcję each. Pętla działa tak długo, aż each wyczerpie całą asocjację i zwróci wartość fałszu: $hash{owoc} = 'pomarańcza'; $hash{kanapka } = klubowa; $hash{picie} = lemoniada; while (($key, $value) = each %hash ) { print "$key: $value\n" ; } picie: lemoniada kanapka: klubowa owoc: pomarańcza Pętla while (<>) ma wbudowaną pewną właściwość przydatną wielu programistom: automatycznie zapisuje wprowadzane dane w zmiennej $_. Oznacza to, że w ciele takiej pętli można zastosować szereg funkcji, które domyślnie używają $_, jak w poniższym przykładzie, gdzie wyświetlane są dane podawane przez użytkownika: while (<>) { print; } Jeśli podany zostanie blok kodu przypisany do frazy continue, będzie uruchamiany po każdym pełnym wykonaniu pętli, zaś użycie polecenia loop wymusza przejście do następnej iteracji pętli (zajrzyj też do punktu „next — wymuszenie następnej iteracji", dalej w tym rozdziale). Używając bloku continue, można sprawić, żeby pętla while zachowywała się dokładnie tak samo, jak pętla for: $loop_index = 1; while ($loop_index <= 5) { print "Hello!\n"; } continue { $loop_index++ ; } Hello! Hello! Hello! Hello! Hello! Zwróć uwagę na to, że while najpierw sprawdza warunek, zatem treść pętli może nie być wykonana ani razu. Jest to wygodne, jeśli wykonanie kodu w przypadku niespełnienia warunku mogłoby spowodować kłopoty, jak w poniższym przykładzie — nie należy wyświetlać wierszy z pliku, gdy uchwyt tego pliku jest nieprawidłowy: while () { print; } Pętla until Pętla until działa tak samo, jak pętla while, ale warunek jest logicznie odwrócony (czyli treść pętli jest wykonywana, dopóki warunek nie jest spełniony). Pętli tej używa się następująco: ETYKIETA until (WYRAŻENIE) BLOK ETYKIETA until (WYRAŻENIE) BLOK continue BLOK Omawiana pętla wykonuje BLOK, póki WYRAŻENIE zwraca fałsz. Oto przykład, w którym znowu wyświetlimy pięć razy wyraz „Hello !\n": $loop_index = 1; until ($loop_index > 5) { print "Hello!\n"; } continue { $loop_index++; } Hello! Hello! Hello! Hello! Hello! Użycie if, unless, until i while do modyfikowania działania instrukcji Omawiane dotąd instrukcje warunkowe i pętle mogą w Perlu także pełnić rolę modyfikatorów, umieszczanych na końcu zwykłych instrukcji: if WYRAŻENIE unless WYRAŻENIE while WYRAŻENIE until WYRAŻENIE Modyfikatory instrukcji działają w zasadzie tak samo, jak samodzielne instrukcje warunkowe lub pętli, ale zwykle łatwiej jest je odczytywać. Poniżej przedstawiamy sposób na poinformowanie użytkownika o tym, że podana przez niego liczba jest zbyt duża, jeśli przekracza 100: while (<>) { print "Zbyt wiele!\n" if $_ > 100; } Oto kolejny przykład z modyfikatorem until, który powoduje wyświetlenie komunikatu błędu, jeśli nie uda się otworzyć pliku: die "Nie mogę otworzyć pliku.\n" unless open($filename); Można oczywiście podobnie utworzyć pętlę while, która pokazuje dane wprowadzane przez użytkownika: print while (<>) ; Pętla do while Wielu programistów uważa, że jeśli tylko funkcjonuje pętla while, to jest również pętla do while, jednak w Perlu jest inaczej. W języku tym nie ma prawdziwej pętli do while, jest natomiast instrukcja do: do BLOK do PROCEDURA(LISTA) # niezalecane do WYRAŻENIE Z drugiej strony instrukcja ta nie jest z natury powiązana w Perlu z pętlami. Pierwsza jej postać, do BLOK, zwraca wartość ostatniej instrukcji z bloku. Druga jest przestarzałą postacią procedury cali. Trzecia postać interpretuje WYRAŻENIE jako nazwę pliku i wykonuje treść pliku: do "myscript.pl"; Jeśli instrukcji do użyjemy w połączeniu z modyfikatorem while, możemy uzyskać pętlę do while (pętla jest wykonywana zawsze co najmniej raz, w przeciwieństwie do zwykłej pętli while): do { print; } while (<>) ; Warto jednak pamiętać, że nie jest to prawdziwa pętla, wobec tego na przykład nie można używać w niej instrukcji omówionych w następnych punktach (next, redo ani last). next — wymuszenie następnej iteracji Polecenie next powoduje natychmiastowe przejście do następnej iteracji pętli i pominięcie wszystkich instrukcji, które mogły jeszcze znajdować się wewnątrz pętli. Instrukcji next używa się w połączeniu z etykietą (tekstem z dwukropkiem na końcu, oznaczającym wiersz w kodzie) — jak w poniższym przykładzie, gdzie wyświetlane są liczby podawane przez użytkownika, o ile tylko nie są one ujemne (to, czy liczba jest ujemna, sprawdzamy tym razem, wyszukując przed nią znak minus): NUMBER: while (<>) { next NUMBER if /^-/: print; } W tym przypadku, jeśli tylko dane wejściowe zaczynaj ą się minusem, przechodzimy do następnej iteracji pętli, niczego nie wyświetlając. W Perlu, z wewnątrz pętli można przejść do dowolnej innej pętli zewnętrznej z etykietą (w przeciwieństwie do języka C, gdzie można przejść tylko do pętli o jeden poziom wyższej). Na przykład możemy z pętli wewnętrznej, oznaczonej jako INNER, przejść bezpśrednio do pętli zewnętrznej OUTER: OUTER: for ($outer = 0; $outer < 10; $outer++) { $result = 0; INNER for ($inner = 0; $inner < 10; $inner++) { $result += $inner * $outer; next OUTER if $inner == $outer; print "$result\n"; } } Zagadnienie związane: Wyrażenia regularne last — zakończenie pętli Polecenie last powoduje natychmiastowe zakończenie wykonywania pętli (jak break w C). Jeśli istnieje blok continue, nie jest już wykonywany. Oto przykład, w którym używamy pętli while do odrzucenia z pliku początkowych komentarzy. Z pętli wychodzimy, stosując polecenie last, uruchamiane w pierwszym wierszu niezaczynającym się od znaku #: # Ten wiersz usunąć # I ten też usunąć COMMENTS: while (<>) { last COMMENTS if !/^#/; } do { print; } while (<>) Jeśli uruchomimy ten skrypt, podając mu jako parametr plik z tymże skryptem (na przykład perl strip.pl strip.pl), w wyniku otrzymamy: COMMENTS: while (<>) { last COMMENTS if !/^#/; } do { print; } while (<>) Zagadnienie związane: Wyrażenia regularne redo — powtarzanie iteracji Polecenie redo powoduje powtórzenie bieżącej iteracji pętli bez ponownego sprawdzania warunku pętli. Jeśli istnieje blok continue, nie jest już uruchamiany. Jednym z zastosowań redo jest analiza danych wejściowych. Załóżmy na przykład, że chcemy uruchomić kod z pliku code.pl, w którym użyto podkreślenia jako znaku kontynuacji wiersza (co jest w Perlu niedozwolone): for ($loopindex = ; _ $loopindex <= 10; _ $loopindex++) { _ print $loopindex; } Do wczytania code.pl i zestawienia go w instrukcję jednowierszową, a następnie uruchomienia uzyskanej instrukcji, można posłużyć się poniższym kodem: while (<>) { if (s/_//g { # wybranie i usunięcie podkreśleń $_ .= <>; redo; } eval; } Jeśli skrypt ten wstawimy na przykład do pliku evaluate, można go użyć następująco: %evaluate code.pl 012345678910 Więcej o funkcji eval dowiesz się w punkcie „Użycie eval do uruchamiania kodu", dalej w tym rozdziale. Instrukcja switch Działanie instrukcji switch polega na dopasowaniu badanej wartości do wartości wzorcowych i uruchomieniu kodu związanego z dobraną wartością— o ile taka została znaleziona. Nie istnieje w Perlu wbudowana instrukcja switch, ale można ją utworzyć za pomocą bloków kodu. Z uwagi na to, że bloki działają jak raz uruchamiane pętle, do zakończenia wykonywania bloku można użyć też instrukcji kontrolnych pętli, takich jak last. Poniżej prezentujemy przykład utworzenia instrukcji switch przy pomocy operatora &&. Drugi argument tego operatora jest uruchamiany tylko wtedy, gdy pierwszy argument jest prawdą. Wartość $_ jest porównywana z różnymi wyrazami, które może podać użytkownik („run", „stop", „connect" i „find"): While(<>) { SWITCH: { /run/ && do { $message = "Uruchomiono\n"; last SWITCH; /stop/ && do { $message = "Zatrzymano\n"; last SWITCH; /connect/ && do { $message = "Połączono\n"; last SWITCH; /found/ && do { $message = "Znaleziono\n"; last SWITCH; }; DEFAULT: $message = "Nieznane polecenie."; } } print $message; Jeśli użytkownik wpisze jedno z przewidzianych poleceń, wyświetli się stosowny komunikat. Inną przydatną alternatywą dla tworzenia instrukcji switch jest stosowanie asocjacji, w której jako kluczy użyto takich wartości, jakie mają być wyszukiwane. Zagadnienie związane: Wyrażenia regularne Użycie goto Perl zawiera instrukcję goto, która opisana tutaj zostanie głównie dla kompletności wykładu, gdyż zwykle jej stosowanie nie jest najlepszym pomysłem — szczególnie, że Perl ma zestaw wygodnych poleceń wyjścia z pętli. Użycie goto powoduje tworzenie skoków, które bardzo trudno jest śledzić, gdyż program nagle jest wykonywany w całkiem innym kontekście. Istnieją trzy postaci instrukcji goto: goto ETYKIETA goto WYRAŻENIE goto &NAZNA Postać goto ETYKIETA powoduje przekazanie sterowania do instrukcji oznaczonej ETYKIETĄ. W drugiej formie zakłada się, że WYRAŻENIE po wyliczeniu da etykietę, do której można będzie przekazać sterowanie. Postać ostatnia jest używana do obsługi procedur. Oto przykład realizacji za pomocą goto pętli odczytującej dane wejściowe, aż użytkownik wpisze „exit": INPUT: $line = <>; if ($line !~ /exit/) {print "Następna próba\n"; goto INPUT} Użycie eval do uruchamiania kodu Instrukcję eval można stosować do wykonywania kodu Perla: eval WYRAŻENIE eval BLOK Oto przykład wykorzystania instrukcji eval do uruchomienia polecenia print "Hello\n": eval "print \"Hello\n\""; Hello Można też uruchamiać szereg kolejnych instrukcji, nawet całe skrypty: eval "print \"Hello \"; print \"there\n\""; Hello there Zobaczmy, jak można uruchamiać interaktywnie instrukcje Perla — byle tylko nie były dłuższe niż jeden wiersz: while (<>) (eval;) Jeśli zostanie pominięte WYRAŻENIE, eval uruchomi zawartość zmiennej $_. W sytuacji, gdy pojawi się błąd, jego komunikat zostanie przekazany w zmiennej $@, co pozwala na prostą realizację obsługi błędów w Perlu (użycie eval jest nawet zalecanym sposobem implementacji obsługi wyjątków). Więcej informacji o instrukcji eval znajdziesz jeszcze dalej w tej książce. Zagadnienie związane Interaktywne uruchamianie skryptów Perla eval - uruchamianie kodu Perla Przechwytywanie błędów wykonania Użycie exit do zakończenia wykonywania programu Instrukcji exit używa się do zakończenia działania programu: exit WYRAŻENIE Jeśli podano WYRAŻENIE, omawiana instrukcja zwraca je jako kod wyjścia programu. Poniższy przykład pokazuje, jak zakończyć program w odpowiedzi na wciśnięcie przez użytkownika litery y: print "Proszę wcisnąć literę y\n"; while (<>) { chop; if ($_ ne 'y') { print "Proszę wcisnąć literę y\n"; } else ( print "Czy zawsze jesteś taki posłuszny?\n"; exit; } } Instrukcja die Funkcja die zapisuje wartość LISTY do pliku STDERR i przerywa działanie programu: die LISTA Oprócz zatrzymania programu, die także zwraca wartość specjalnej zmiennej Perla $!. Wewnątrz instrukcji eval komunikat o błędzie jest zapisywany w specjalnej zmiennej $@ i instrukcja eval kończy swoje działanie. Oto przykład obsłużenia próby otwarcia nieistniejącego pliku: $filename = "nonexist.pl"; open FileHandle, $filename or die "Pliku $filename nie można otworzyć\n"; Skrypt zakończy swoje działanie komunikatem o błędzie: Pliku nonexist.pl nie można otworzyć Rozdział 6. Wyrażenia regularne W skrócie Obsługa tekstu to naprawdę mocna strona Perla, a w dużej mierze stanowią o tym wyrażenia regularne. Umożliwiają one dopasowywanie wzorców (czyli porównywanie tekstu ze specjalnym wzorcem, który może zawierać znaki ogólne i inne znaki specjalne) oraz podstawianie tekstu, co daje programiście ogromne możliwości przetwarzania tekstu. Z drugiej strony nie ma wątpliwości, że wyrażenia regularne w Perlu są czymś, co od-stręcza wielu programistów. Przyswojenie sobie nawet dość prostych wyrażeń regularnych wymaga sporo czasu — przykładem może być choćby wyrażenie poniższe, które pasuje do tekstu od znaczników HTML i do odpowiednich znaczników końcowych, odpowiednio oraz wraz z wszystkim, co się między znacznikiem początkowym a końcowym znajduje: $text = "Tutaj jest hiperłącze."; if ($text =~ /<([IMG[A])>[\w\s\.]+<\/\l>/i) {print "Znaleziono znacznik obrazka lub łącza.";} Znaleziono znacznik obrazka lub łącza. W tym rozdziale postaramy się przedstawić w dostępny sposób jeden z najtrudniejszych tematów Perla. Użycie wyrażeń regularnych Z wyrażeniami regularnymi używa się dwóch operatorów: m// (dopasowującego wzorzec) oraz s/// (służącego do podstawiania). Przyjrzymy się w tym rozdziale jeszcze jednemu blisko spokrewnionemu operatorowi, tr///, który pozwala realizować proste przekształcenia, choć bez użycia wyrażeń regularnych. Operator m// Operator m// próbuje dopasować podany wzorzec db tekstu — domyślnie tekstu ze zmiennej $_. W poniższym przykładzie wyszukujemy — w podanym przez użytkownika tekście — słowa „exit" (litera i po drugim ukośniku powoduje, że podczas wyszukiwania wielkość liter nie ma znaczenia): while (<>) { if(m/exit/i) {exit;} } Do wskazania wyszukiwanego wzorca można też zastosować operator =~, jak poniżej, gdzie nakazano użyć zmiennej $line (poniższy kod nie zmienia wartości tej zmiennej): while($line = <>) { if($line =~ m/exit/1) {exit;} } Dzięki użyciu operatora !~ (zamiast =~) można zmienić znaczenie porównania. Powoduje to zanegowanie znaczenia =~. Z uwagi na to, że operator m// jest w Perlu tak często stosowany, można nawet pominąć literę m: while($line = <>) { if($line =~ /exit/1) {exit;} } Operator s/// Operator s/// umożliwia podmienianie tekstu. Załóżmy na przykład, że chcemy zamienić wystąpienia słowa młody na stary: $text = "Dość młody."; $text =~ s/młody/stary/; print $text; Dość stary. Operator ten domyślnie także korzysta z $_, podobnie jak m//. Nie trzeba używać akurat ukośników, ważne jest jedynie, aby wybrany znak był konsekwentnie stosowany w całym wyrażeniu: $text = "Dość młody."; $text =~ s|młody|stary|; print $text; Dość stary. Można nawet użyć nawiasów: $text = "Dość młody."; $text =~ s(młody)(stary) ; print $text; Dość stary. Trzeba pamiętać, że operatory m// i s/// zaczynają dopasowywanie od lewej strony: $text = "Dość młody, ale nie aż tak młody."; $text =~ s/młody/stary/; print $text; Dość stary, ale nie aż tak młody. Operator tr/// Oprócz operatorów m// i s/// Perl obsługuje jeszcze operator tr///, który umożliwia pewnego rodzaju przekształcanie tekstu, jak poniżej, gdzie zastępujemy wszystkie wystąpienia litery „o" literą „i": $text = "Ma na imię Tom."; $text =~ tr/o/i/; print $text; Ma na imię Tim. W Perlu operator tr/// znaczy to samo, co y///. Są to po prostu dwie nazwy tego samego operatora. Właśnie powyższymi trzema operatorami — m//, s/// i tr/// zajmiemy się dalej w tym rozdziale. Na razie dokonaliśmy krótkiego wprowadzenia , ale teraz czas zająć się dokładnym omówieniem wyrażeń regularnych. Zagadnienia: Wyrażenia regularne Wyrażenia regularne przekazywane są operatorom m// i s///, jak poniżej, gdzie użyto wyrażenia regularnego \b ([A-Za-z ]+) \b do wybierania z tekstu słów: $text = "Tematem jest Perl."; $text =~ /\b([A-Za-z]+)\b/; print $1; Tematem W tym przypadku wyrażenie \b([A-Za-z]+)\b zawiera metaznaki grupujące ( i ) oraz metaznaki ograniczające \b, klasę znaków [A-Za-z] (oznaczającądowolną literę wielką lub małą) oraz kwalifikator +, który informuje, że podany znak musi wystąpić jeden lub więcej razy. Z uwagi na to, że wyrażenia regularne, takie jak pokazane w poprzednim przykładzie, są dość skomplikowane, w tym rozdziale najpierw zaczniemy je rozkładać na części pierwsze. Wyrażenia regularne składają się z następujących elementów: * zwykłych znaków, * klas znaków, * alternatywnych wzorców, * kwantyfikatorów, * asercji, * odwołań, * rozszerzeń wyrażeń regularnych. Każdy z powyższych punktów wart jest uwagi i w następnej części rozdziału dokładniej je omówimy. Zwykłe znaki w wyrażeniach regularnych Pojedyncze znaki w wyrażeniach regularnych reprezentują same siebie, chyba że sąme-taznakami o specjalnym znaczeniu (jak $ czy A). Oto przykład, w którym sprawdzamy, czy użytkownik wpisał „zakończ", a jeśli tak, to kończymy działanie programu: while (<>) { if (m/zakończ/) {exit;} } Lepiej byłoby sprawdzić, czy słowo „zakończ" było jedynym wyrazem podanym przez użytkownika (być może użytkownik napisał „Tylko teraz nie zakończ!"). Aby sprawdzić, czy nie występują inne dane, używa się znaków A i $, poza tym dodamy modyfikator i, aby uniezależnić się od wielkości liter: while(<>) { if (m/^zakończ$/i) {exit;} } Więcej informacji o znakach " i $ znajdziesz, w punkcie „Asercje w wyrażeniach regularnych", zaś o modyfikatorze i w punkcie „Modyfikatory operatorów m// i s///", dalej w tym rozdziale. W Perlu — oprócz zwykłych znaków — w wyrażeniach regularnych używa się znaków specjalnych: \077 — znak zapisany ósemkowo, \a — sygnał dźwiękowy, \c — znak kontrolny, \d — dowolna cyfra, \D — dowolny znak, który nie jest cyfrą, \E — koniec modyfikacji wielkości liter, \e — escape, \f — nowa strona, \l — następny znak ma być małą literą, \L — stosowanie małych liter aż do napotkania \E, \n — znak nowego wiersza, \Q — cytowanie (czyli wyłączenie) interpretacji metaznaków wzorca, aż do napotkania \E, \r — powrót kursora, \S — dowolny czarny znak, \s — dowolny biały znak, \t — tabulator, \u — następny znak ma być wielką literą, \U — stosowanie wielkich liter, aż do napotkania \E, \w — znak słowa (znaki alfanumeryczne i podkreślenie), \W — dowolny znak, który nie jest znakiem słowa, \xl — znak zapisany szesnastkowo. Zwróć uwagę na \w pasujące do znaków słowa, gdyż dają one ogromne możliwości. Warto pamiętać, że \w pasuje do pojedynczego znaku alfanumerycznego, a nie całego słowa. W celu dobrania całego słowa należy użyć zapisu \w+: $text = "Oto fragment tekstu."; $text =~ s/\w+/Oto/; print $text; Oto fragment tekstu. Znak + oznacza „jedno lub więcej dopasowań", a zostanie on dokładniej omówiony dalej, w punkcie „Kwantyfikatory w wyrażeniach regularnych". Dopasowanie do dowolnego znaku Istnieje w Perlu jeszcze jeden bardzo istotny znak wyrażeń regularnych: kropka (.). Znak ten pasuje do dowolnego znaku poza znakiem nowego wiersza. Na przykład można zastąpić gwiazdkami wszystkie znaki w tekście, przy czym trzeba użyć też modyfikatora g wymuszającego podstawienie globalne: $text = "Teraz już czas."; $text =~ s/./*/g; print $text; *************** Co zrobić w sytuacji, kiedy chcemy wybrać właśnie kropki? Znaki takie, jak kropka, pełnią w wyrażeniach regularnych rolę metaznaków (w Perlu są to: \|()[{^$*+?.) i żeby taki znak wybrać faktycznie z tekstu, należy grf poprzedzić we wzorcu lewym ukośnikiem. Spójrzmy na poniższy przykład: $line = ".Hello!"; if ($line =~ m/^\./) { print "Zdania nie zaczyna się od kropki!"; } Zdania nie zaczyna się od kropki! Klasy znaków w wyrażeniach regularnych Znaki można pogrupować w klasy, przy czym taka klasa odpowiada dowolnemu znakowi do niej należącemu. Klasę znaków zamyka się nawiasami kwadratowymi. Używając kreski - można wskazać zakres znaków. Aby dopisać do klasy znak „-" jako taki, należy użyć zapisu \-. Poniższy przykład kodu służy do wyszukiwania samogłosek alfabetu łacińskiego: $text = "Oto fragment tekstu."; if ($text =~ /[aeiou]/) {print "Są tu samogłoski.\n";} Są tu samogłoski. Oto z kolei przykład, gdzie wyszukujemy pierwszego słowa (wzorzec [A-za-z]+) i zastępujemy je słowem Perl: $text = "Co jest tematem."; $text =~ s/[A-Za-z]+/Perl/; print $text; Perl jest tematem. Symbol + oznacza ,jeden lub więcej znaków" — więcej informacji na ten temat znajdziesz w punkcie „Kwantyfikatory w wyrażeniach regularnych". Jeśli w klasie jako pierwszy znak użyty zostanie (^), do klasy należeć będą wszystkie znaki niepodane w nawiasach, jak w poniższym przykładzie, gdzie dobieramy wszystkie znaki oprócz liter i spacji: $text = "Ta książka nie ma 998 stron."; $text =~ s/[^A-Za-z\s]+/999/; print $text; Ta książka nie ma 999 stron. Alternatywne wzorce Aby wskazać alternatywne wzorce, należy rozdzielić je znakiem i. Oto przykład, w którym sprawdzamy, czy użytkownik wpisał jedno ze słów: „exit", „quit" lub „stop": while(<>) { if (m/exit|quit|stop/) {exit;} } Często wzorce alternatywne umieszcza się w nawiasach, aby jasne było, gdzie taka alternatywa się zaczyna i gdzie się kończy, a także aby nie zostały do niej włączone przypadkowo znaki sąsiednie. W poniższym przykładzie metaznaki ^ i $ oznaczają początek i koniec wiersza (więcej o nich dowiesz się w punkcie „Asercje w wyrażeniach regularnych"): while(<>) { if (m/^(exit|quit|stop)$/) {exit;} } Alternatywy są sprawdzane od strony lewej do prawej, a użyta zostanie pierwsza znaleziona możliwość. Należy pamiętać, że w nawiasach kwadratowych znak | jest traktowany jako zwykły znak, zatem wzorzec [Tim|Tom|Tam] jest równoważny wzorcowi [Tioam|]. Kwantyfikatory w wyrażeniach regularnych Kwantyfikatory można wykorzystać w celu poinformowania, że wzorzec ma wystąpić określoną liczbę razy. Na przykład kwantyfikator + może zostać użyty do wybrania i zastąpienia jednej lub więcej liter „e": $text = "Witamy w Peeeeeeeeeeeeeeeerlu."; $text =~ s/e+/e/g; print $text; Witamy w Perlu. Oto zestawienie kwantyfikatorów wyrażeń regularnych Perla: * — wystąpienie dowolną liczbę razy (od zera poczynając), + — wystąpienie co najmniej raz, ? — wystąpienie raz lub wcale, {n} — wystąpienie n razy, {n,} — wystąpienie co najmniej n razy, (n,m} — wystąpienie co najmniej n razy, ale nie więcej niż m razy. W poniższym przykładzie sprawdzamy, czy podano co najmniej 20 znaków: while (<>) { if ( !m/.{20,}/) (print "Proszę o dłuższe wiersze! \n" ; } } „Zachłanność" kwantyfikatorów Kwantyfikatory domyślnie są bardzo „zachłanne", co oznacza, że zwracają najdłuższy fragment pasujący do wzorca. Jeśli na przykład chcielibyśmy zamienić angielskie zdanie „That is some text, isn't it?" na jego skróconą postać „Thafs some text, isn't it?", czyli zastąpić „That is" przez „That's", można by spróbować to zrobić następująco: $text = "That is some text, isn't it?"; $text =~ s/.*is/That's/; print $text; Pojawia się jednak problem — zachłanny kwantyfikator * próbuje dopasować tak duży fragment, jak tylko jest to możliwe. Wobec tego .* (poprzedzające słowo „is") pasuje do wszystkich znaków — aż do ostatniego wystąpienia „is", zatem wynik będzie taki: That'sn't it? O tym, jak uniknąć tej zachłanności kwantyfikatorów, powiemy więcej w dalszej części rozdziału, w punkcie „Ograniczanie apetytu kwantyfikatorów". Wyrażenia regularne z kwantyfikatorami mogą także realizować proces powrotu. Aby wyrażenie regularne pasowało do tekstu, musi ono pasować w całości, a nie tylko jego część. Jeśli początek wyrażenia z kwanty fikatorem jest odpowiedni, ale dalsza część już nie, Perl się częściowo wycofuje z dopasowania początku i próbuje znaleźć inne dopasowanie — takie, które pozwoli dopasować całość wyrażenia. Ten właśnie proces nazywamy powrotem. Asercje w wyrażeniach regularnych W wyrażeniach można użyć asercji, aby wyrazić pewne warunki logiczne. Poniżej znajduje się lista tych asercji (pamiętaj, że wszystkie mają długość zero, co znaczy, że nie powoduj ą wy dłużenia dopasowanego tekstu): ^ — początek wiersza, $ — koniec wiersza (czyli końcowy znak nowego wiersza), \b — granica słowa, \B — (nie) granica słowa, \A — początek tekstu, \Z — koniec tekstu lub tuż przed końcowym znakiem nowego wiersza, \z — koniec tekstu, \G — pasuje tylko do tego, co m//g pozostawiło (działa tylko z \g), (?=WYRAŻENIE) — pasuje tylko wtedy, gdy dalej pasuje WYRAŻENIE, (?!WYRAŻENIE) — pasuje tylko wtedy, gdy dalej WYRAŻENIE nie pasuje, (?<=WYRAŻENIE) — pasuje tylko, jeśli wcześniej dopasowano WYRAŻENIE, (?) { if (m/^(tak) $/) {print "Dziękuje za współprace."} } Odwołania do poprzednich dopasowań Czasem wygodne jest móc się odwołać do poczynionego wcześniej dopasowania z tego samego wyrażenia regularnego. Załóżmy na przykład, że przetwarzamy kod HTML i musimy być pewni, że znacznikom początkowym odpowiada znacznik końcowy, na przykład odpowiada . Do poprzedniego dopasowania z tego samego wyrażenia można odwoływać się, podając cyfrę po lewym ukośniku, czyli \1, \2, \3 i tak dalej. Oto kod, który wynajduje znaczniki końcowe pasujące do i : $text = "Tutaj jest hiperłącze."; if ($text =~ /<([IMG|A])>[\w\s\.]+<\/\1>/i) {print "Znaleziono znacznik obrazka lub łącza.";} Znaleziono znacznik obrazka lub łącza. Można też przygotować fragment wzorca, aby później do niego się odwołać — należy go w tym celu ująć w nawiasy podwzorca (nawiasy te nazywamy operatorem grupowania). Później do takiego dopasowania można się odwołać poprzez podanie cyfry za znakiem $, na przykład $1, $2, $3 i tak dalej. Oto przykład zastosowania $1: $text = "Mam 4 jabłka."; if ($text =~ /(\d+)/) {print "Liczba jabłek: $1.\n";} Liczba jabłek: 4. W następnym przykładzie, za pomocą operatora s/// odwracamy kolejność trzech $text = "Teraz cię widzę"; $text =~ s/^(\w+) *(\w+) *(\w+)/$3 $2 $1/; print $text; widzę cię Teraz Oprócz odwołań można też użyć specjalnej zmiennej Perla, $&, która zawiera poprzednie dopasowanie. Element &' wskazuje słowo za ostatnim dopasowaniem, zaś &' wskazuje słowo przed ostatnim dopasowaniem. Rozszerzenia wyrażeń regularnych W Perlu istnieje rozszerzona składnia wyrażeń regularnych, w której używa się nawiasów ze znakiem zapytania. Oto rozszerzenia już dostępne: • (#tekst) — komentarz. Całe takie wyrażenie j est pomij ane, • (?:wzorzec) lub (?imsx-imsx:wzorzec) —grupy podwyrażeń działające jak ( i ), ale nie jest tworzone odwołanie, • (?=WYRAŻENIE) —asercja w przód, pasuje, jeśli WYRAŻENIE pasuje dalej, • (?!WYRAŻENIE) — zaprzeczenie asercji w przód, pasuje, jeśli WYRAŻENIE nie pasuje nigdzie dalej, • (?<=WYRAŻENIE) — asercja w przód, pasuje, jeśli WYRAŻENIE pasuje wcześniej, • () { if(m/^stop$/i) {exit;} } Operator tr/// — translacja Tekst można przetwarzać nie tylko dzięki operatorom m// i s///, ale także zastosowaniu operatora tr///, zwanego też y///: tr/LISTA/LISTA/ y/LISTA/LISTA/ Operator ten używany jest do translacji tekstu, czyli zamiany jednego znaku z pierwszej listy na odpowiadający mu znak z drugiej listy, jak poniżej, gdzie zamieniamy litery „o" na „i": $text = "Mam na imię Tom."; $text =~ tr/o/i/; print $text; Mam na imię Tim. Podobnie jak m// i s///, również tr/// domyślnie używa $_: while (<>) { tr/o/i/; print; } Można też podać zakresy znaków, jak w poniższym przykładzie, gdzie zmieniamy tekst na wielkie litery: $text = "Oto jest tekst."; $text =~ tr/a-z/A-Z/; print $text; OTO JEST TEKST. Operator tr/// zwraca liczbę przekształceń (translacji), zatem czasem można zobaczyć następujący kod, który zlicza wystąpienia „x" w $_: $text = "Oto jest text."; $xcount = ($text =~ tr/x/x/); print $xcount; 1 Użycie modyfikatorów operatora tr/// W Perlu można użyć z tr/// następujących modyfikatorów: c — uzupełnienie wyszukiwanej listy, d — usunięcie niepodmienionych znaków, s — usunięcie powtórzeń podmienionych znaków. Dopasowywanie słów Dzięki zastosowaniu \s pasującego do czarnych znaków można dopasowywać słowa: $text = "Teraz już nadszedł czas."; $text =~ /(\S+)/; print $1; Teraz Jednak \s może pasować do dowolnych znaków niealfanumerycznych. Aby tego uniknąć, warto użyć \w, które pasuje do znaków alfanumerycznych i podkreślenia: $text = "Teraz już nadszedł czas."; $text =~ /(\w+)/; print $1; Teraz Jeśli chodzi o pobranie tylko liter, należy skorzystać z klasy znaków: $text = "Teraz już nadszedł czas."; $text =~ / ([A-Za-z]+)/; print $1; Teraz Bezpieczniejszą techniką osiągnięcia tych samych wyników jest użycie ograniczników słów, jak poniżej \b: $text = "Teraz już nadszedł czas."; $text =- /(\b[A-Za-z]+\b)/; print $1; Teraz Dobieranie początku wiersza Do początku wiersza można się odwołać, stosując znak ^. Zobaczmy, jak odszukać kropkę znajdującą się na początku zdania: $line = ".Hello!"; if ($line =- m/^\./) { print "Zdania nie zaczyna się od kropki!"; } Zdania nie zaczyna się od kropki! Aby odwołać się do kropki, a nie do dowolnego znaku, konieczne jest poprzedzenie tej kropki lewym ukośnikiem. Dobieranie końca wiersza W celu odwołania się do końca wiersza, używa się znaku $, jak w poniższym przy-kładzie, w którym sprawdzamy, czy użytkownik w wierszu podał słowo „exit" (i nic poza nim): while(<>) { if ) { chomp; unless (/^q/i) { print; } else { exit; } } Wycinanie części ciągu Stosując operator grupowania (), można wyciąć z tekstu jego część (można też oczywiście użyć wbudowanej funkcji Perla substr). W poniższym przykładzie z tekstowej bazy danych pobierany jest rodzaj towaru: $record = "Numer towaru: 12345 Typ towaru: drukarka Cena towaru: 1350zł"; if ($record =~ /Typ towaru: *([a-z]+)/i) {print "Typ towaru to $1\n";} Typ towaru to drukarka Wywołania funkcji i wyrażeń Perla w wyrażeniach regularnych Używając modyfikatora e, można poinformować, że prawy argument operatora s/// jest wyrażeniem, które należy ewaluować. Oto na przykład sposób użycia funkcji Perla uc do zmiany wszystkich słów na wielkie litery: $text = "Czas już, czas!"; $text =~ s/(\w+)/uc($1)/ge; print $text; CZAS JUŻ, CZAS! Dobranie n-tego dopasowania Wykorzystując modyfikator g, można dobrać wszystkie wystąpienia tekstu, ale co zrobić, jeśli chodzi nam tylko o jedno z nich — na przykład drugie lub trzecie? Możemy użyć kolejnych dopasowań w pętli while — z wykorzystaniem operatora grupowania (), jak to pokazano poniżej: $text = "Imię: Anna Imię: Burkart Imię: Klara Imię: Daniel"; $match = 0; while ($text =~ /Imię: *(\w+)/g { +$match; print "Dopasowanie $match to $1.\n"; } Dopasowanie l to Anna. Dopasowanie 2 to Burkart. Dopasowanie 3 to Klara. Dopasowanie 4 to Daniel. Można ten sam przykład zapisać za pomocą pętli for: $text = "Imię: Anna Imię: Burkart Imię: Klara Imię: Daniel"; for ($match = 0; $text =~ /Imię: *(\w+)/g; print print "Dopasowanie $match to $1.\n" {} Dopasowanie l to Anna. Dopasowanie 2 to Burkart. Dopasowanie 3 to Klara. Dopasowanie 4 to Daniel. Ograniczanie apetytu kwantyfikatorów Kwantyfikatory domyślnie są „zachłanne", czyli dobierają możliwie najwięcej znaków, byle tylko tekst pasował do wyrażenia regularnego. W poniższym przykładzie próbujemy zamienić „That is" na „Thafs", ale wyrażenie .*is pasuje do tekstu zaczynającego się wraz z początkiem wiersza, a kończącego się ostatnim w tym wierszu wystąpieniem słowa "is", podczas gdy nam chodziło o pierwsze wystąpienie tego słowa: $text = "That is some text, isn't it?"; $text =~ s/.*is/That's/; print $text; That'sn't it? Jeśli chcemy ograniczyć apetyt kwantyflkatorów, czyli sprawić, aby powodowały one dopasowanie do możliwie najkrótszego tekstu, możemy za kwantyfikatorem użyć pytajnika ?: *? — dowolna liczba wystąpień, w tym zero, +? — co najmniej jedno wystąpienie, ?? — zero lub jedno wystąpienie, {n}? — n wystąpień, {n,}? — co najmniej n wystąpień, {n,m}? — co najmniej n wystąpień, co najwyżej m. Poniżej przedstawiamy wyniki: $text = "That is some text, isn't it?"; $text =~ s/.*?is/That's/; print $text; That's some text, isn't it? Usuwanie spacji wiodących i końcowych Aby usunąć początkowe spacje, warto użyć następującego wyrażenia: $text = " Czas już, czas!"; $text =~ s/^\s+//; print $text; Czas już, czas! W celu usunięcia spacji końcowych możemy wykorzystać z kolei następujące wyrażenie: $text = "Czas już, czas! "; $text =~ s/\s+$//; print $text; Czas już, czas! Użycie asercji do sprawdzania w przód i wstecz W Perlu zdefiniowano asercje działające w przód i wstecz (wszystkie one w wyrażeniu regularnym maj ą długość zero): (?=WYRAŻENIE) —asercja wprzód, pasuje, jeśli WYRAŻENIE pasuje dalej, (? ! WYRAŻENIE) — zaprzeczenie asercji w przód, pasuje, jeśli WYRAŻENIE nie pasuje dalej, (><=WYRAŻENIE) —asercja w przód, pasuje, jeśli WYRAŻENIE pasuje wcześniej, (? 10) { print "Wartością jest $value.\n"; } else { print "Wartość jest za mała.\n"; } $value = 12; if ($value > 10) { print "Wartością jest $value.\n"; } else { print "Wartość jest za mała.\n"; } Wartość jest za mała. Wartością jest 12. Lepiej jednak byłoby cały blok if ująć w procedurę, którą tym razem nazwiemy printifOK: sub printifOK { my $internalvalue = shift(@_); if ($internalvalue > 10) { print "Wartością jest $internalvalue.\n"; } else { print "Wartość jest za mała.\n"; } } Procedurom można przekazywać wartości, a są one umieszczane w specjalnej tablicy @_. Reszta kodu wygląda tak samo, jak podany wcześniej blok if. Używając dwukrotnie tej procedury i przekazując jej przy tym odpowiednie wartości, możemy uzyskać taki sam wynik, jak poprzednio: $value = 10; printifOK ($value); $value = 12; printifOK ($value); Wartość jest za mała. Wartością jest 12. Procedury mogą też zwracać wartości, jak poniżej, gdzie używamy instrukcji return do zwrócenia sumy dwóch przekazanych wartości: sub addem { ($valuel, $value2) = @_; return $value1 + $value2; } print „2 + 2 = „ addem (2, 2) . "\n" 2 + 2 = 4 W innych językach — oprócz procedur — definiuje się też funkcje i to tylko funkcje zwracać mogą wartości. Jednak w Perlu wartość może zwrócić też procedura, zaś specjalne funkcje po prostu nie istnieją — w tym języku nazw „procedura" i „funkcja" można używać wymiennie. Tyle wiadomości o procedurach na początku nam wystarczy. Warto zauważyć, że procedury umożliwiają rozbicie kodu na szereg częściowo samodzielnych fragmentów, którym przekazuje się dane, a z tych fragmentów można także odczytywać informacje zwrotne. Taki podzielony kod łatwiej jest pisać i później konserwować, często jest on też znacznie krótszy. Teraz zajmiemy się szczegółowymi zagadnieniami. Zagadnienia: Deklarowanie procedur Deklaracji można użyć, aby poinformować Perla o istnieniu procedur, w tym o liczbie i typie spodziewanych argumentów i zwracanej wartości. Deklarowania funkcji nie powinieneś mylić z jej definiowaniem, które oznacza podanie kodu, tworzącego treść procedury. W przeciwieństwie do innych języków, w Perlu nie jest konieczne deklarowanie procedur przed ich użyciem — chyba że mają zostać zastosowane bez ujmowania ich parametrów w nawiasy (na przykład jako operatory list): wtedy przed użyciem procedury trzeba ją zadeklarować. Oto różne sposoby deklarowania procedur w Perlu: sub NAZWA; sub NAZWA ( PROTOTYP) ; sub NAZWA BLOK sub NAZWA (PROTOTYP) BLOK Deklarując procedurę, można podać jej prototyp, który określa parametry procedury. Niektórzy programiści chętnie stosują prototypy jako mechanizm kontroli kodu — więcej informacji na ten temat znajdziesz w punkcie „Prototypy". Istnieje też możliwość importowania procedur z pakietów Perla: use PAKIET qw(NAZWAl NAZNA2 NAZWA3) ; Procedurze możemy nadać dowolną nazwę, trzeba jednak pamiętać, że Perl rezerwuje nazwy procedur zapisane samymi wielkimi literami dla procedur wywoływanych niejawnie (czyli przez samego Perla) — czego przykładem mogą być procedury BEGIN i END w pakietach. Na początku nazwy każdej procedury znajduje się znak &, który jest niejawną częścią tej nazwy, można jednak ten znak pomijać. Jeśli zatem procedura nazwana zostanie count, można wywoływać ją jako count(l,2) lub jako &count(l,2). Dodatkowe informacje o wywoływaniu procedur odnajdziesz w punkcie „Wywoływanie procedur". Prototypy Niektórzy programiści chętnie stosują prototypy jako kontrolę tego, czy procedury są prawidłowo wywoływane — na przykład, czy zamiast tablicy nie został przekazany skalar. W celu zadeklarowania prototypu w odpowiedniej kolejności wylicza się znaki, od których zaczynać się muszą parametry procedury. Znak $ oznacza skalary, znak @ — tablice i tak dalej. Przykłady podano w tabeli 7.1. Warto zwrócić uwagę na użycie w prototypie znaków @ i % — taki parametr zapamiętuje dalsze parametry, jak poniżej: sub NAZWA($$@) Taka deklaracja informuje, że procedurę tę można wywołać, podając dwa skalary i za nimi listę: NAZWA $skalarl, $skalar2, $tablical, $tablica2, $tablica3; Aby upewnić się, że parametry zaczynają się od @ lub %, znaki te należy poprzedzić lewym ukośnikiem: NAZWA(\@) Tabela 7.1. Prototypy procedur Deklaracja Sposób wywołania sub NAZWA($) NAZWA $parametrl; sub NAZWA($$) NAZWA $parametrl, $parametr2; sub NAZWA($$;$) NAZWA $parametrl, $parametr2, $opcjonalny; sub NAZWA(@) NAZWA $tablical, $tablica2, $tablica3; sub NAZWA($@) NAZWA $parametrl, tablical, $tablica2; sub NAZWA(\@) NAZWA @parametrl; sub NAZWA(\%) NAZWA %{$wsk_asocjacji} ; sub NAZWA(&) NAZWA proceduraanonimowa; sub NAZWA(*) NAZWA *parametrl; sub NAZWA() NAZWA; Teraz można wywołać procedurę, podając jako parametr tablicę: NAZWA @tablica; Można też używać parametrów opcjonalnych, które od parametrów obowiązkowych oddzieła się średnikami, co też pokazano w tabeli 7.1. Prototypy wpływają jedynie na interpretację wywołań funkcji, jeśli ich nazwy nie zostaną poprzedzone znakiem &. Definiowanie procedur Kod treści procedury umieszcza się w jej definicji, aby zaś tę definicję zacząć, używa się słowa kluczowego sub: sub NAZWA BLOK sub NAZWA (PROTOTYP} BLOK Oto przykład zdefiniowania procedury printhello, która po prostu wyświetla tek „Hello!". Kod tej procedury jest cały zamknięty między nawiasami klamrowymi — { }: sub printhello { print "Hello!"; } Omawianą procedurę można wywołać następująco: printhello; Hello! Dokładniej wywoływaniem procedur zajmiemy się w następnym punkcie. Wywoływanie procedur Po zdefiniowaniu procedury można ją wywołać, podając jej parametry: &NAZWA(LISTAPARAMETRÓW); Jak zwykle w Perlu, można wywołanie zrealizować też inaczej. Jeśli użyjemy nawiasów, możemy pominąć początkowy znak & : NAZWA (LISTAPARAMETRÓW); Kiedy procedura jest wywoływana, jej parametry są umieszczane w tablicy @_. Jeśli procedura zostanie wywołana przy użyciu znaku & i bez listy parametrów, czyli w postaci &NAZWA, bieżąca postać zmiennej @_ jest przekazywana procedurze. To wygodne rozwiązanie, jeśli jedna procedura jest wywoływana z innej i należy przekazać jej te same argumenty, które otrzymała pierwsza procedura (użycie wywołania z & wyłącza także kontrolę parametrów wywołania według prototypu). Parametry — przekazywane procedurze — są przekształcane na zwykłą listę, jeśli zatem przekazane zostaną dwie tablice, ich elementy będą zestawione w jedną listę. Aby przekazać tablicę, zachowując jej strukturę, należy zastosować wskaźniki (zajrzyj do punktu „Przekazywanie przez referencję" w tym rozdziale). Odczytywanie parametrów procedury Parametry przekazywane procedurze można odczytać ze specjalnej tablicy @_, która do tego właśnie służy. Jeśli na przykład procedurze przekazane zostaną dwa parametry, w kodzie tej procedury można się do nich odwoływać jako $_ [0] oraz $_[1]. W sytuacji, gdy na przykład chcemy w procedurze addem dodać do siebie dwie liczby i wyświetlić wynik, wywołanie będzie miało postać addem (2,2) . Wartości przekazanych parametrów można pobrać z tablicy @_: sub addem { $valuel = @_[0] ; $value2 = @_[1] ; print "$valuel + $value2 = " . ($valuel + $value2) . "\n"; } Teraz procedurę wywołujemy następująco: addem (2,2); 2 + 2 = 4 Do pobierania wartości z @_ można też użyć funkcji shift: sub addem { $valuel = shift @_; $value2 = shift @_; ' print "$valuel + $value2 = " . ($valuel + $value2) . "\n"; } Można również od razu przypisać wartości z @_ liście zmiennych: sub addem { ($valuel, $value2) = @_; print "$valuel + $value2 = " . ($valuel + $value2) . "\n"; } Zmienna liczba parametrów W Perlu łatwo jest używać procedur ze zmienną liczbą parametrów, gdyż wszystkie parametry przekazywane są w tablicy @_. Aby określić liczbę przekazanych wartości, wystarczy sprawdzić wielkość tej tablicy za pomocą $#_. Jeśli należy przetwarzać wszystkie elementy tablicy @_, można użyć pętli f oreach, nie ma przy tym znaczenia liczba tych elementów: sub addem { $sum = 0; foreach $element (@_) { $sum += $element; } print join (" + ", @_) . " = " . $sum; } addem( 2, 2, 2); 2 + 2 + 2 = 6 Użycie domyślnych wartości parametrów Z uwagi na to, że użytkownik może przekazywać zmienną liczbę parametrów, wygodne może być podanie wartości domyślnych parametrów, które użytkownik może pominąć. Używa się do tego operatora ||=: sub addem { ($valuel, $value2) = @_; $value2 ||= l; print "$valuel + $value2 = " . ($valuel + $value2); } W tym przypadku podano wartość domyślną l dla zmiennej $value2. Wartość ta zostanie użyta, jeśli po przypisaniu tablicy wartością tej zmiennej będzie zero. Oto wynik wywołania procedury z wartością 2: addem(2); 2 + 1 = 3 Przy zastosowaniu opisanej techniki zakłada się, że użytkownik nie przekazuje wartości zero ani pustych znaków. Lepszym rozwiązaniem jest jawne sprawdzenie liczby wartości w tablicy @_ za pomocą wyrażenia $#_: sub addem { $valuel = shift @_; if ($#_ > 0) { $value2 = @_[1] ; } else { $value2 = 1; } print "$valuel + $value2 = " . ($valuel + $value2) ; } addem (2); 2 + 1 = 3 Wartości zwracane przez procedury (funkcje) Wartością zwracaną przez procedurę jest wartość ostatniego wyrażenia. Można też do zakończenia wykonywania procedury użyć instrukcji return, podając jej zwracaną wartość (w niektórych językach programowania funkcje zwracają wartość, zaś procedury już nie, ale w Perlu procedura i funkcja są tym samym). Zwracana wartość jest przekształcana stosownie do kontekstu (lista, skalar lub wartość pusta), w zależności od tego, w jakim kontekście procedurę wywołano. Oto przykład, w którym dodajemy do siebie przekazane procedurze wartości i zwracamy ich sumę: sub addem { ($valuel, $value2) = @_; return $valuel + $value2; } print "2 + 2 = " . addem2, 2) . "\n"; 2 + 2 = 4 Zauważmy, że możemy też zwrócić listę wartości: sub getvalues { return l, 2, 3, 4, 5, 6; } Tego typu listy zwracanych wartości można również przypisać tablicy: @array = getvalues; print join(", ", @array) ; l, 2, 3, 4, 5, 6 Jeśli jednak zwracana jest więcej niż jedna tablica lub asocjacja, zostaną one wszystkie przekształcone w jedną listę. Oznacza to, że przypisanie dotyczy tylko jednej tablicy, zatem poniższa konstrukcja nie zadziała: (@arrayl, @array2) = getvalues; W tym przypadku wszystkie wartości z getvalues zostaną umieszczone w @arrayl. W punkcie „Przekazywanie przez referencję" opisano, jak sobie z tym problemem poradzić. Użycie my do określania zasięgu W Perlu domyślnie zmienne są globalne, co oznacza, że można korzystać z nich w dowolnym miejscu programu (w rzeczywistości są globalne w ramach pakietu — więcej o pakietach w rozdziale 15.). W związku z tym — nawet w przypadku definiowania zmiennych wewnątrz procedury — są one globalne i można sięgnąć do nich po zakończeniu wykonywania się procedury, jak w poniższym przykładzie, gdzie wyświetlamy wartość zmiennej $inner poza procedurą (drugi wyraz „Hello!" pochodzi bezpośrednio ze zmiennej $inner): sub printem { $inner = shift @_; print $inner; } printem "Hello!\n"; print $inner; Hello! Hello! Gdyby to już było wszystko, Perl szybko by zginął wskutek nadmiaru zmiennych globalnych, zaśmiecających programy. Jednak można ograniczyć zasięg zmiennej do procedury. Użycie słowa kluczowego my powoduje, że zmienna jest związana z otaczającym ją blokiem, instrukcją warunkową, podprocedurą, instrukcją eval lub plikiem, który uruchomiono, stosując do, reąuire lub use. Zmienne zadeklarowane jako my są ograniczone leksykalnie, zaś zmienne zadeklarowane jako local są tworzone dynamicznie. Podstawowa różnica polega na tym, że zmienne tworzone dynamicznie są widoczne także w procedurach wywoływanych wewnątrz zasięgu zmiennej, podczas gdy zmienne ograniczone leksykalnie — nie (więcej na ten temat dowiesz w punkcie „local — zmienne tymczasowe"). Jeśli po my podany zostanie więcej niż jeden element, lista musi zostać ujęta w nawiasy. Wszystkie elementy użyte z my muszą być poprawnymi wyrażeniami lvalue. Oprócz tego zakres leksykalnie można określać tylko dla identyfikatorów alfanumerycznych, zaś wbudowane elementy, jak $_, lokalnie trzeba deklarować przy pomocy słowa kluczowego local. Oto przykład, w którym ograniczamy zakres zmiennej $inner do procedury. Do zmiennej tej nie można już się odwołać poza procedurą (instrukcja print $inner wyświetla pusty znak): sub printem { my $inner = shift @_; print $inner; } printem "Hello!\n"; print $inner; Hello! Oto kilka przykładów zastosowania my. Pamiętaj, że my dotyczy tylko skalarów, tablic i asocjacji: my $variablel; my ($variablel, $variable2) ; my $variablel = 5; Jeśli z my jest deklarowana więcej niż jedna zmienna, konieczne staje się otoczenie ich nawiasami. W szczególności należy unikać pomyłki takiej, jak w poniższym kodzie, gdzie tylko jedna zmienna, $variablel, jest deklarowana jako my: my $variablel, $variable2 = 5; Zmienne ograniczone leksykalnie nie muszą być zamknięte akurat w bloku kodu, mogą należeć też do instrukcji kontrolnych. Na przykład zmienna $variablel — zadeklarowana jako my — jest dostępna we wszystkich blokach instrukcji if: $testvalue = 10; if ((my $variablel = 10) > $testvalue ) { print "Wartość $variablel jest większa od wielkości ", "wzorcowej . \n" ; } elsif ($variablel < $testvalue) { print "Wartość $variablel jest mniejsza od wielkości ", "wzorcowej . \n" ; } else { print "Wartość $variablel jest równa wielkości wzorcowej . \n"; Wartość 10 jest równa wielkości wzorcowej. Zagadnienie związane: Tworzenie pakietu Żądanie leksykalnego ograniczenia zmiennych Możemy zażądać leksykalnego ograniczenia zmiennych i w takiej sytuacji stosujemy dyrektywę use strict 'vars'. Jeśli zostanie ona użyta, wszelkie wskaźniki na zmienne, od chwili użycia dyrektywy do końca bloku lub zakresu, powinny albo odwoływać się do zmiennej leksykalnej, albo muszą być kwalifikowane nazwą pakietu. W bloku znajdującym się wewnątrz bloku z deklaracją use strict ' vars' można z kolei odwołać żądanie leksykalnego ograniczenia, stosując dyrektywę no strict 'vars'. Local — zmienne tymczasowe Oprócz zmiennych ograniczonych leksykalnie, tworzonych za pomocą słowa kluczowego my, można też tworzyć zmienne ograniczone dynamicznie, stosując słowo kluczowe local. Słowo local pozwala tworzyć tymczasowe kopie zmiennych globalnych, a z taką kopią można pracować aż do chwili wyjścia poza zakres dostępności tej kopii (wtedy jest przywracana do użycia zmienna globalna). Często zapewne natkniesz się na wielkie afisze głoszące, że zamiast local trzeba używać my, ale tak naprawdę w celu realizacji pewnych rzeczy konieczne jest użycie local zamiast my — na przykład, aby utworzyć lokalną kopię zmiennych specjalnych, jak $_, zmienić jeden z elementów tablicy lub asocjacji, ewentualnie używać lokalnie uchwytów plików i formatów Perla. Warto pamiętać, że słowo kluczowe local nie tworzy nowej zmiennej, po prostu tworzy kopię zmiennej globalnej, której można od tej chwili używać. Oto kilka przykładów zastosowania local: local $variablel; local ($variablel, $variable2); local $variablel = 5; local *FILEHANDLE; Użycie local tworzy kopie podanych zmiennych (są to kopie lokalne w danym bloku), instrukcji eval czy instrukcji do oraz wszelkich procedur wywoływanych z takiego bloku. Jeśli podany zostanie więcej niż jeden element, trzeba je ujmować w nawiasy. Należy zaznaczyć, że wszystkie elementy muszą być poprawnymi wartościami lvalue. Zmienne trwale (statyczne) Czasem potrzebne jest zachowanie wartości zmiennej między kolejnymi wywołaniami procedury. Jeśli jednak zmienna zostanie zadeklarowana ze słowem kluczowym my, będzie zerowana przy każdym wejściu do procedury, co widać w poniższym przykładzie. Zmienna $count jest ustawiana na zero i zwiększana do jedynki przy każdym wywołaniu procedury incrementcount, wobec czego zamiast sekwencji l, 2, 3, 4 otrzymujemy cztery jedynki: sub incrementcount { my $count; return ++$count; } print incrementcount . "\n"; print incrementcount . "\n"; print incrementcount . "\n"; print incrementcount . "\n"; 1 1 1 1 Uczynienie $count zmienną statyczną — jak w języku C — problem nasz rozwiązuje, gdyż zmienne statyczne zachowuj ą swój e wartości między kolejnymi wywołaniami procedury. Jednak w Perlu zmienne statyczne nie są obsługiwane bezpośrednio; zmienne globalne są domyślnie statyczne, ale zmienne lokalne procedur deklarowane z my — nie. Jest i na to metoda, wystarczy odrobina sprytu. Zmienne leksykalne nie są zerowane, póki nie nastąpi wyjście poza ich zakres, zatem problem można rozwiązać, zwiększając ich zakres. W poniższym przykładzie przenosimy deklarację zmiennej wraz z my poza procedurę, a cały kod (ową deklarację oraz samą procedurę) ujmujemy w nawiasy klamrowe, przez co tworzymy z nich blok na tym samym poziomie, co wywołania procedury: { my $count = 0; sub incrementcount { return ++$count; } } print incrementcount . "\n"; print incrementcount . "\n"; print incrementcount . "\n"; print incrementcount . "\n"; 1 2 3 4 Można też wszystko zamknąć w bloku BEGIN, który jest uruchamiany przy ładowaniu programu: sub BEGIN { my $count = 0; sub incrementcount { return ++$count; } print incrementcount . "\n" print incrementcount . "\n" print incrementcount . "\n" print incrementcount . "\n" 1 2 3 4 Zagadnienie związane: BEGIN — konstruktor pakietu Rekurencyjne wywoływanie procedur Procedury Perla można wywoływać rekurencyjnie, czyli procedura może wywoływać samą siebie. Typowym przykładem procedury rekurencyjnej jest wyliczanie silni (na przykład 5! = 5*4*3*2*l), zatem tego przykładu użyjemy. Problem wyliczania silni rozdzielimy na kolejne kroki rekurencyjne, a w każdym kroku przemnożymy przekazaną wartość przez wynik wyliczenia silni liczby o jeden mniejszej. Jeśli jednak procedura zostanie wywołana z jedynką, od razu zwrócimy wynik: sub factorial { my $value = shift (@_); if ($value == 1) { return $value; } else { return $value * factorial ($value - 1); } } $result = factorial(6); print $result; 720 Jak widać, procedura może sama się rekurencyjnie wywoływać — w tym przypadku jest to konieczne do wyliczenia zwracanej wartości (no, chyba że jako parametr przekazana zostanie jedynka). Zagnieżdżanie procedur W Perlu można też zagnieżdżać procedury, czyli definiować jedne procedury wewnątrz innych. Oto przykład, w którym definiujemy procedurę outer i wewnątrz niej definiujemy kolejną procedurę inner: sub outer { sub inner { print "Wewnątrz procedury wewnętrznej inner.\n"; } inner; } Wewnątrz procedury wewnętrznej inner. Przekazywanie przez referencję Przekazywanie tablic lub asocjacji powoduje ich „spłaszczenie" w jedną długą listę, co jest problemem, jeśli należy przekazać dwie lub więcej osobnych tablic lub asocjacji. Aby zachować ich strukturę, można przekazać do nich wskaźnik (referencję — więcej informacji o wskaźnikach znajdziesz w rozdziale 8.). Oto przykład dwóch tablic: @a = (l, 2, 3); @b = (4, 5, 6) ; Załóżmy, że chcemy napisać procedurę addem, która będzie dodawać tablice — element po elemencie — niezależnie od ich wielkości. W tym celu funkcja addem musi dostawać jako parametry dwa wskaźniki na tablice: @array = addem (\@a, \@b); W samej addem pobieramy wskaźniki do tablic i następnie tworzymy pętlę na tych tablicach (podany kod będzie jaśniejszy po zapoznaniu się z następnym rozdziałem), a wówczas zwracana jest tablica zawierająca sumę przekazanych tablic, wyliczaną element po elemencie: sub addem { my (refl, $ref2) = @_; for ($loop_index = 0; $loop_index <= $#{$refl}; $loop_index++) { $result[$loop_index] = @{$refl)[$loop_index] + @{$ref2)[$loop_index]; } return @result; } Oto sposób użycia funkcji addem do zsumowania dwóch tablic: @array = addem (\@a, \@b); print join (', ', $array); 5, 7, 9 Warto zauważyć, że przekazywanie przez referencję umożliwia bezpośrednie odwoływanie się do przekazanych danych, dzięki czemu możliwe jest modyfikowanie danych w procedurze wywoływanej. Skalary są w Perlu domyślnie przekazywane przez referencję, zatem nie trzeba dodawać referencji jawnie, a i tak można w procedurze modyfikować ich wartości. Zagadnienie związane: Tworzenie wskaźnika Przekazywanie pozycji z tablicy symboli Przekazywanie typów ogólnych było jedyną metodą przekazywania przez referencję w Perlu, nadal jest to najlepszy sposób przekazywania danych, takich jak uchwyty plików. Typy ogólne są tak naprawdę pozycjami tablicy symboli, jeśli zatem następuje przekazywanie typu ogólnego, faktycznie przekazywany jest wskaźnik do wszystkich typów danych przechowywanych pod daną nazwą. Oto przykład realizacji procedury addem z poprzedniego punktu, tym razem jednak zamiast wskaźników użyto typów ogólnych: @a=(l, 2, 3); @b=(4, 5, 6); sub addem { local (*array, *array2) = @_; for ($loop_index = 0; $loop_index <= $łarrayl; $loop_index++) { $result [$loop_index] = @arrayl [$loop_index] + @array2 [$loop_index] ; } return @result; } @result = addem (*a, *b) ; print join(", ", @result) ; 5, 7, 9 Jeśli odbywa się przekazywanie uchwytów plików, można użyć typu ogólnego, jak *STDOUT, ale lepiej zastosować wskaźniki, gdyż będą one działać także wtedy, gdy zostaną użyte dyrektywy takie jak use strict 'ref s' (dyrektywy to polecenia dla kompilatora, a dyrektywa tutaj podana sprawdza wskaźniki symboliczne — więcej informacji na ten temat znajdziesz w rozdziale 8.). Oto przykład przekazania procedurze *STDOUT: sub printhello { my $handle = shift; print $handle "Hello!\n"; } printhello (\*STDOUT) ; Hello! Użycie wantarray do sprawdzenia kontekstu wyjściowego Procedury mogą zwracać skalary lub listy wartości, co oznacza, że mogą być wywoływane w dowolnym kontekście. W przypadku konieczności obsługiwania obu kontekstów, trzeba wiedzieć, jaka wartość ma być zwrócona — do tego służy funkcja wantarray. Funkcja wantarray zwraca wartość true, jeśli zwracana wartość będzie zinterpretowana w kontekście list, a f alse w przeciwnym wypadku. Prześledźmy przykład użycia wantarray. Nasza procedura, swapxy, zastępuje wszystkie litery „x" literami „y". Jeśli kontekst wyjściowy jest kontekstem listowym, zwracana jest tablica, w przeciwnym razie zwracana jest wartość skalarna: sub swapxy { my @data = @_; for (@data) { s/x/y/g; } return wantarray ? @data : $data[0]; } Procedury swapxy można użyć, przekazując jej listę i tę samą listę od niej odbierając: @a = "xyz"; @b = "xxx"; ($a, $b) = swapxy($a, $b); print "$a\n"; print "$b\n"; yyz yyy Funkcje wywoływane Jeśli prototyp funkcji jest typu (), funkcja ta może zostać przez kompilator Perla uruchomi. Funkcje wywoływane są zoptymalizowane pod kątem szybkości wykonania, ale są bardzo ograniczone i muszą składać się ze stałej lub leksykalnie ograniczonego ska-lara (bez jakichkolwiek dalszych wskaźników). Oprócz tego nie można się do nich odwoływać przez & ani do, gdyż takie wywołania nigdy nie są włączane. W poniższym przykładzie funkcje byłyby uruchomione (funkcja exp Perla zwraca liczbę e, podstawę algorytmu naturalnego, podniesioną do potęgi określonej parametrem. Funkcja exp l daje lepsze przybliżenie e, niż stała użyta w pierwszej procedurze): sub e () (2.71828) sub e () (exp 1} Nadpisywanie funkcji wbudowanych i użycie CORE Nadpisanie procedury oznacza podanie nowej jej definicji. Procedury można nadpisy-wać, co dotyczy także funkcji wbudowanych Perla, ale możliwe jest to tylko w przypadku procedur zaimportowanych z modułów (samo zadeklarowanie procedury nie wystarcza). Możemy jednak użyć dyrektywy subs w celu zadeklarowania procedur, a później te zaimportowane nazwy nadpisać, w ten sposób nadpisując funkcje wbudowane. W poniższym przykładzie nadpisujemy funkcję exit Perla, powodując, że przed zakończeniem działania programu prosimy użytkownika o potwierdzenie: use subs 'exit'; sub exit { print "Czy na pewno chcesz zakończyć?"; $answer = O; if ($answer =~ /^t/i) {CORE::exit;} } while (1) { exit; } Jeśli użytkownik jest zdecydowany zakończyć działanie programu, używamy pseudo-pakietu GORE, wywołując CÓRĘ: :exit (w Perlu do kwalifikowania nazwy pakietem używa się podwójnego dwukropka). Pseudopakiet CÓRĘ zawsze zawiera oryginalne wersje funkcji wbudowanych i jeśli funkcje takie zostaną nadpisane, zawsze można się do nich odwołać przez ten pakiet. Tworzenie procedur anonimowych Perl umożliwia tworzenie procedur anonimowych (czyli bez nazwy) przez utworzenie wskaźnika na procedurę (więcej o wskaźnikach w następnym rozdziale): $coderef = sub {print "Hello!\n";}; Średnik na końcu powyższej instrukcji jest obowiązkowy, choć w przypadku definiowania zwykłej procedury byłby zbędny. Procedurę powyższą można wywołać, używając symbolu & do wskazania, że chodzi o wywołanie procedury, i ujmując wskaźnik w nawiasy klamrowe: &{ $coderef ); Hello! Zagadnienie związane: Tworzenie wskaźnika Tworzenie tablic procedur Tablica procedur zawiera wskaźniki na procedury, a także umożliwia wywoływanie procedur przy użyciu indeksu lub klucza, co jest przydatne w przypadku zbiorów, których poszczególne elementy muszą być obsługiwane przez różne procedury. Załóżmy na przykład, że mamy dwie procedury do przeliczania temperatury, podanej w stopniach Celsjusza, na stopnie Fahrenheita — i odwrotnie: sub ctof # Celsjusze na Fahrenheity { $value = shift(@_); return 9 * $value / 5 + 32; } sub ftoc # Fahrenheity na Celsjusze { $value = shift(@_); return 5 * (Svalue - 32) / 9; } Aby funkcje te wstawić do tablicy procedur, konieczne jest utworzenie do nich wskaźników: $tempconvert[0] = \&ftoc; $tempconvert[1] = \&ctof; Teraz można za pomocą indeksu wskazywać, o którą procedurę chodzi: print "Zero stopni Celsjusza to " . &{$tempconvert[1]}(0) . " stopni Fahrenheita.\n"; Zero stopnia Celsjusza to 32 stopni Fahrenheita. Parametry można przekazywać tak wywoływanym procedurom, umieszczając te parametry w nawiasach za wskaźnikiem na procedurę. Zagadnienie związane: Tworzenie wskaźnika Rozdział 8. Wskaźniki w Perlu W skrócie Wskaźniki to nowy, podstawowy typ Perla po raz pierwszy wprowadzony dopiero w wersji 5. Wskaźniki Perla są bardzo podobne do wskaźników języków takich, jak C. Jak sugeruje sama nazwa, wskaźniki wskazują na dane, aby zaś sięgnąć do samej danej, trzeba zastosować dereferencję wskaźnika. Wskaźników można używać do tworzenia złożonych struktur danych, czym zajmiemy się w rozdziale 13. Wskaźniki są stosowane w Perlu w bardzo wielu sytuacjach, na przykład do tworzenia anonimowych tablic, asocjacji i procedur, do tworzenia szablonów funkcji — wszystkie te zastosowania omówimy w tym rozdziale. Kiedy należy przekazać procedurze więcej niż jedną tablicę lub asocjację, zachowując strukturę tych parametrów (czyli nie przekształcając ich wszystkich w jedną długą listę), przekazuje sieje przez referencję, czyli jako wskaźniki. W sytuacji, gdy tworzy się obiekty, wywołując konstruktor klasy, konstruktor ten zwykle zwraca wskaźnik na utworzony obiekt, a nie sam obiekt, co oznacza, że wskaźniki są też podstawowym typem danych w przypadku programowania obiektowego w Perlu. Istnieją dwa rodzaje wskaźników: wskaźniki bezpośrednie oraz wskaźniki symboliczne. Rozdział ten zaczniemy od przyjrzenia się obu tym typom. Wskaźniki bezpośrednie Załóżmy, że mamy zmienną $variablel: $variablel = 5; Aby utworzyć bezpośredni na nią wskaźnik, używa się lewego ukośnika: $variablel = 5; $reference = \$variablel; Zmienna $reference posiada teraz wskaźnik na $variablel (zawierający adres zmiennej $variablel oraz jej typ — skalar). Taki wskaźnik jest nazywany wskaźnikiem bezpośrednim. Warto zauważyć, że może on wskazywać na dowolny skalar Perla, natomiast do dereferencji (określenia wskazywanej wartości) używa się operatora $. W poniższym przykładzie właśnie za pomocą operatora $ określamy wartość, do której odnosi się $reference: $variablel = 5; $reference = \$variablel; print $$reference; Dereferencja wskaźnika pozwala uzyskać pierwotną wartość danej: $variablel = 5; $reference = \$variablel; print $$reference; 5 Zastanówmy się, co by się stało, gdybyśmy spróbowali się odwołać bezpośrednio do wskaźnika? W takim przypadku zobaczymy adres i typ zmiennej $variablel w przestrzeni danych interpretera Perla: $variablel = 5; $reference = \$variablel; print $reference; SCALAR(Ox8a57d4) Tak właśnie wskaźniki bezpośrednie są przechowywane przez Perla. Wskaźniki symboliczne Wskaźnik symboliczny nie zawiera adresu ani typu fragmentu danych, ale zawiera nazwę danych. Załóżmy na przykład, że mamy zmienną z poprzedniego przykładu: $variablel = 5; Nazwę tej zmiennej można zapisać w innej zmiennej, którą nazwiemy $variablename (zwróć uwagę na pominięcie wyróżnika $): $variablel = 5; $variablename = "variablel"; Dereferencja nazwy zmiennej przekształca tę nazwę we wskaźnik na dane zawarte w zmiennej; wskaźnik taki nazywamy wskaźnikiem symbolicznym. Oto sposób użycia operatora $ w omówionej sytuacji: $variablel = 5; $variablename = "variablel" ; print " $$variablename\n " ; Do dereferencji wskaźników — oprócz operatora $ — można też używać operatora -> (zwanego „strzałką"). Operator strzałki Innym często stosowanym operatorem jest strzałka. Operatora tego można użyć do wskazania indeksu, klucza lub listy parametrów w przypadku wskaźnika do tablicy, asocjacji lub procedury: $arrayreference = [l, 2, 3]; print $arrayreference->[0]; 1 Więcej o tym operatorze powiemy dalej w rym rozdziale. Zauważ, że w tym przykładzie użyliśmy wskaźnika do tablicy, ale sama tablica nie miała nazwy — to jest właśnie tablica anonimowa. Anonimowe tablice, asocjacje i procedury Za pomocą wskaźników można tworzyć tablice, asocjacje i procedury, nie podając ich nazw. Oto przykład utworzenia anonimowej tablicy i przypisania tej tablicy zmiennej wskaźnikowej $arrayreference: $arrayreference = [l, 2, 3]; Teraz można odwoływać się do wartości z tej tablicy, stosując dereferencję wskaźnika: $arrayreference = [l, 2, 3]; print $$arrayreference[0]; 1 Używając konstrukcji takich jak tablice anonimowe, będziemy mogli tworzyć tablice tablic. Posmakujesz tego dalej w tym rozdziale, a teraz czas zająć się szczegółami wskaźników. Zagadnienia: Tworzenie wskaźnika Wskaźniki można tworzyć, stosując operator \ (lewy ukośnik). Tak utworzone wskaźniki są wskaźnikami bezpośrednimi (wskaźniki bezpośrednie można też uzyskać za pomocą tablicy symboli — więcej na ten temat dowiesz się dalej w tym rozdziale, w punkcie „Tworzenie wskaźników na podstawie tablicy symboli". Można również tworzyć wskaźniki symboliczne, co opisano w punkcie „Tworzenie wskaźników symbolicznych"). Lewego ukośnika można użyć do skalara, tablicy, asocjacji, procedury lub wartości. Oto przykład utworzenia bezpośredniego wskaźnika na wartość: $reference = \"Hello! "; Aby za pomocą takiego wskaźnika sięgnąć do pierwotnej wartości, należy użyć operatora dereferencji $: $reference = \"Hello!"; print $$reference; Hello! Mechanizm ten możemy także dowolnie zagnieżdżać. Łatwo wyobrazić sobie sytuację, w której na przykład utworzymy następujący wskaźnik na wskaźnik na wskaźnik na wskaźnik: $reference4 = \\\\"Hello!"; Nie zmieniają się też zasady użycia operatora $ przy dereferencji: print $$$$$reference4; Hello! Możemy się odwoływać nie tylko do podanych bezpośrednio wartości, tak samo można tworzyć wskaźniki na zmienne, tablice, asocjacje, procedury i tak dalej (pamiętaj, że wskaźniki są przechowywane w zmiennych skalarnych): $scalarreference = \$zmiennal; $arrayreference = \@tablica; $hashreference = \%asocjacja; $codereference = \&procedura; $globreference = \*nazwa; Wskaźniki pojawiają się nawet wtedy, gdy robi się ich dereferencję, jak w przykładzie poniższym, gdzie używamy wskaźnika, który aż dotąd nie istniał — $ref erence: $$reference = 5; print "$$reference\n"; 5 Po uruchomieniu tego kodu pojawia się wskaźnik $ref erence —jest to proces nazywany w Perlu utworzeniem automatycznym: print "$reference\n"; SCALAR(0x8a0bl4) Oto przykład użycia wskaźników, w którym przekazujemy procedurze wskaźniki do dwóch tablic. Procedura dodaje tablice element po elemencie (zakładamy, że obie tablice są tej samej wielkości) i zwraca tablicę wynikową. Przekazywanie procedurze wskaźników zamiast samych tablic pozwala uniknąć „spłaszczenia" tablic w jedną długą listę: @a = (l, 2, 3) ; @b = (4, 5, 6) ; sub addem { my (referencel, $reference2) = @_; for ($loop_index = 0; $loop_index <= $#$referencel; $loop_index++) { $result[$loop_index] = @{$referencel}[$loop_index] + @{$reference2}[$loop_index]; } return @result; } @array = addem (\@a, \@b); print join (', ', @array); 5, 7, 9 Tworzenie wskaźników na tablice anonimowe Jak pokazano na początku tego rozdziału, można utworzyć tablicę bez nazwy, określaną jako tablica anonimowa — wystarczy użyć konstruktora tablic, pary nawiasów kwadratowych: $arrayreference = [l, 2, 3]; Konstruktor tablicy anonimowej zwraca wskaźnik na tablicę anonimową, zapisywany w zmiennej $arrayreference. Dereferencja pozwala sięgać do poszczególnych elementów tablicy: $arrayreference = [l, 2, 3]; print $$arrayreference[0]; 1 Dereferencję tablicy można też zrealizować za pomocą operatora „strzałka": $arrayreference = [l, 2, 3]; print $arrayreference->[0]; 1 Więcej informacji na ten temat znajdziesz w punkcie „Dereferencja przy użyciu operatora „strzałka", dalej w tym rozdziale. Tablice anonimowe umożliwiają programistom Perla realizację przydatnych sztuczek związanych z interpolacją wywołania procedury czy umieszczaniem wyników wyrażenia w ujętym w podwójne cudzysłowy tekście. W poniższym przykładzie użyto funkcji u c do zamiany tekstu na wielkie litery: print "@{[uc(hello)]} there.\n"; Hello there. Perl traktuje @{} jako blok, a w trakcie interpretowania tego bloku jest tworzony wskaźnik na tablicę anonimową. Tablica ta ma jeden tylko element — wynik wyrażenia lub funkcji w niej umieszczonej. Po dereferencji wskaźnika na tablicę wynik jest wstawiany w tekst. Tworzenie wskaźników na asocjacje anonimowe Można też tworzyć wskaźniki na nienazwane asocjacje, czyli asocjacje anonimowe. Stosuje się w tym celu konstruktor asocjacji, parę nawiasów klamrowych. Oto przykład, w którym tworzymy wskaźnik na asocjację anonimową i umieszczamy w tej asocjacji dwie pary klucz + wartość: $hashref = { Name => Tommie, ID => 1234, }; Teraz można takiej anonimowej asocjacji używać jak dowolnej innej asocjacji — trzeba tylko pamiętać o uprzedniej dereferencji: print $$hashref{Name}; Tommie Do dereferencji wskaźnika na asocjację możemy również zastosować operator „strzałka": $hashref = { Name => Tommie, ID => 1234, }; print $hashreference->{Name}; Tommie Więcej informacji na ten temat znajdziesz w punkcie „Dereferencja przy użyciu operatora „strzałka"" w dalszej części tego rozdziału. Tworzenie wskaźników na procedury anonimowe Za pomocą konstruktora procedury — słowa kluczowego sub — można utworzyć wskaźnik na procedurę bez nazwy, czyli procedurę anonimową: $codereference = sub {print "Hello!\n"}; Warto zauważyć, że w tym przypadku obowiązkowy jest średnik na końcu, który w przypadku definicji normalnej procedury jest zbędny. Aby taką procedurę wywołać, wystarczy dereferencja wskaźnika i poprzedzenie całego wyrażenia znakiem &: &$codereference; Hello! Takim anonimowym procedurom możemy też przekazywać parametry: $codereference = sub (print shift}; &{$codereference} ("Hello!\n"); Hello! Można w końcu do dereferencji wskaźnika na procedurę użyć operatora „strzałka", jak poniżej (więcej informacji na ten temat znajdziesz w punkcie „Dereferencja przy użyciu operatora „strzałka""): $codereference = sub {print shift}; $codereference->("Hello!\n"); Hello! Tworzenie wskaźników na podstawie tablicy symboli Tablica symboli Perla jest asocjacją zawierającą wskaźniki na wszystkie symbole pakietu, indeksowane kluczami takimi, jak SCALAR, HASH, CODE i tak dalej. Oznacza to, że jeśli potrzebujemy wskaźnika na dowolny obiekt Perla, to można go pobrać z tablicy symboli nawet bez konieczności używania operatora \. Oto przykład pobierania wskaźników na różne typy danych z tablicy symboli. W tym przypadku do pobierania wskaźników używamy typu ogólnego (czyli pozycji z tablicy symboli): $scalarreference = *nazwą{SCALAR}; $arrayreference = *nazwa{ARRAY}; $hashreference = *nazwa{HASH}; $codereference = *nazwa{CODE}; $ioreference = *nazwa{IO}; $globreference = *nazwa{GLOB}; Poniżej przedstawiamy sposób pobrania wskaźnika na zmienną $variablel i użycia tego wskaźnika do wyświetlenia wartości zmiennej: $variablel = 5; $scalarreference = *variablel{SCALAR}; print $$scalarreference; 5 Zobaczmy teraz, jak pobrać i użyć wskaźnika na procedurę: sub printem { print "Hello!\n"; } $codereference = *printem{CODE}; &$codereference; Hello! Użycie wyrażenia *nazwa{IO} powoduje zwrócenie uchwytu wejścia-wyjścia, czyli uchwytu pliku, gniazda (socket) lub uchwytu katalogu. Wskaźnika na uchwyt wejścia-wyjścia nie można uzyskać za pomocą lewego ukośnika. Warto też zauważyć, że opisywana metoda pobierania wskaźników powoduje, że pracujemy z już istniejącymi wcześniej symbolami. Jeśli symbol, którego próbuje się użyć, nie istnieje, wyszukiwanie wartości w tablicy symboli zwróci jedynie wartość undef (we wcześniejszych wersjach Perla, jeśli użyto wyrażenia typu $nowyskalar (SCALAR) i $nowyskalar nie był wcześniej używany, zwracany był wskaźnik na anonimowy skalar, jednak zostało to zmienione). Dereferencja wskaźnika Operatora $ używamy do dereferencji wskaźnika, czyli do pobrania wskazywanego przez wskaźnik elementu. Przykłady pokazano już wcześniej w tym rozdziale: $variablel = 5; $reference = \$variablel; print $$reference; 5 Operatora dereferencji $ można użyć tam, gdzie można zastosować identyfikator lub listę identyfikatorów. Oto kilka przykładów pokazujących sposób dereferencji podstawowych typów Perla: $scalar = $$scalarreference; @array = @$arrayreference; %hash = %$hashreference; &$codereference($argumentl, $argument2); *glob = *$globreference; Można także — co już widzieliśmy wcześniej — tworzyć wiele poziomów dereferencji: $reference4 = \\\\"Hello!"; print $$$$$reference4; Hello! Dereferencje mogą być też bardziej złożone. Chodzi na przykład o sytuację, kiedy w przypadku wskaźnika na tablicę możemy zastosować indeks: @array = (l, 2, 3); $arrayreference = \@array; print $$arrayreference[0]; 1 Z kolei w przypadku wskaźnika na asocjację można użyć klucza: %hash = ( Name => Tommie, ID => 1234, ); $hashreference = \%hash; print $$hashreference{Name}; Tommie W przypadku wskaźnika na procedurę można zastosować listę parametrów: sub printem { print shift; } $codereference = \sprintem; &$codereference ("Hello!\n"); Hello! Bezpośrednio podany wskaźnik możesz zamienić także na blok zwracający wskaźnik — oto poprzednie przykłady zapisane z użyciem bloków: $scalar = ${$scalarreference}; @array = @{$arrayreference}; %hash = %{$hashreference}; &{$codereference{($argumentl, $argument2); *glob = *{$globreference}; Warto tu jeszcze wspomnieć, że typy ogólne mogą podlegać dereferencji tak samo, jak wskaźniki, gdyż typy ogólne zawierają wskaźniki na wszystkie typy danych związane z nazwą. Podczas dereferencji wskaźnika zawsze podaje się żądany typ danej, jak poniżej, gdzie przedrostek $ sugeruje, że chodzi o skalar: $variable = 5; print ${*variablel}; 5 Z kolei poniżej potrzebna jest tablica, o czym informuje przedrostek @: @array = (l, 2, 3); print join (", ", @{*array}); l, 2, 3 Dereferencja przy użyciu operatora „strzałka" Podczas pracy z tablicami, asocjacjami i procedurami, wykonywanie dereferencji znakomicie ułatwia użycie operatora „strzałki". Spójrzmy, jak na przykład zastosować ten operator do wskaźnika na tablicę: $arrayreference = [l, 2, 3]; print $arrayreference->[0]; 1 Można też tworzyć tablice tablic, jak w poniższym przykładzie, w którym tworzymy anonimową tablicę anonimowych tablic: $arrayreference = [[l, 2, 3], [4, 5, 6]]; Aby odwołać się do elementów takiej tablicy tablic, należy użyć składni, którą przedstawiamy poniżej: $arrayreference = [[l, 2, 3], [4, 5, 6]]; print $arrayreference->[l][1]; 5 Więcej informacji o tablicach tablic znajdziesz w rozdziale 13. Operatora „strzałki" można także użyć, korzystając ze wskaźników na asocjacje: $hashreference->( klucz ) = "To jest jakiś tekst."; print $hashref erence->{klucz}; To jest jakiś tekst. W tym przykładzie polegamy na perłowym procesie automatycznego tworzenia, gdyż w chwili użycia wskaźnika zakładamy, że istnieje wskazywany obiekt. Oto sposób użycia strzałki do wskaźnika na procedurę: sub printem { print shift; } $codereference = \&printem; $codereference->("Hello! \n") ; Hello! Po lewej stronie strzałki może znajdować się dowolne wyrażenie zwracające wskaźnik, na przykład: $dataset[$today]->{ceny}->[1234] = "$4999.99"; Zagadnienie związane: Deklarowanie tablic tablic Pomijanie operatora „strzałki" Operator „strzałki" jest w Perlu opcjonalny, jeśli występuje między nawiasami kwadratowymi i klamrowymi, zatem przykład z poprzedniego punktu: $dataset[$today]->{ceny}->[1234] = "$4999.99"; można zapisać też w sposób następujący: $dataset[$today]{ceny}[1234] = "$4999.99"; Perl umożliwia pominięcie strzałki między nawiasami głównie po to, aby ułatwić pracę z tablicami tablic, a także aby ich zapis był podobny, jak w innych językach programowania. Oto przykład: @array = ( [l, 2], [3, 4], ); print $array[l][1]; 4 Zagadnienie związane: Tworzenie tablic tablic Użycie operatora ref do określania typu wskaźnika Operator ref pozwala określić, jakiego typu element wskazuje wskaźnik: ref WYRAŻENIE ref Operator ten zwraca niezerową wartość, jeśli WYRAŻENIE jest wskaźnikiem, i zero, jeśli nim nie jest (gdy WYRAŻENIE nie zostanie podane, sprawdzona zostanie wartość zmiennej $_). Zwrócona niezerową wartość określa typ wskazywanego obiektu. Typy wbudowane są następujące: REF (wskaźnik) SCALAR (skalar) ARRAY (tablica) HASH (asocjacja) CODE (procedura) GLOB (typ ogólny) Oto przykład, w którym używamy operatora ref dla wskaźnika do wielkości skalarnej: $variablel = 5; $scalarref = \$variablel; print (ref $scalarref); SCALAR Tworzenie wskaźników symbolicznych Wskaźniki bezpośrednie zawierają faktyczny adres wskazywanego obiektu oraz jego typ, natomiast wskaźniki symboliczne zawierają nazwę tego obiektu. Prześledźmy przykład, w którym tworzymy wskaźnik symboliczny na zmienną $variablel i używamy tego wskaźnika, aby sięgnąć do wartości tej zmiennej (należy pamiętać, że wskaźniki symboliczne zawierają samą nazwę zmiennej, bez wyróżnika $): $variablel = 0; $variablename = "variablel"; $$variablename = 5; print "$variablel\n"; 5 Tak jak w przypadku wskaźników bezpośrednich, odwołanie się we wskaźniku symbolicznym do nieistniejącego obiektu powoduje jego utworzenie. Poniżej — na przykład — zmienna $variablel nie istniała, póki się do niej nie odwołaliśmy: $variablename = "variablel"; ${$variablename} =5; print "$variablel\n"; 5 Można też tworzyć wskaźniki symboliczne do asocjacji i tablic: $arrayname = "array1"; $arrayname->[1] = 5; print "$array1[1]\n"; a nawet procedur: $subroutinename = "subroutine1"; sub subroutinel { print "Hello!\n"; } &$subroutinename(); Hello! We wskaźnikach symbolicznych możemy odwoływać się jedynie do zmiennych globalnych i lokalnych z bieżącego pakietu. Zmienne ograniczone leksykalnie (deklarowane ze słowem kluczowym my) nie należą do tablicy symboli, dlatego nie można ich tu używać. W poniższym przykładzie jako wartość wskazywanej zmiennej zostanie pokazany pusty znak: my $variablel = 10; $variablename = "variablel"; # mamy problem print "Wartością jest $$variablename\n"; # Powyższy kod da niepełny wynik: Wartością jest Wyłączanie wskaźników symbolicznych Zdarza się, że chcemy użyć wskaźnika bezpośredniego, ale przez pomyłkę stosujemy wskaźnik symboliczny (czyli po prostu nazwę wskazywanego obiektu). Aby uniemożliwić stosowanie wskaźników symbolicznych, można użyć dyrektywy kompilatora: use strict 'refs'; Po wykorzystaniu tej dyrektywy Perl pozwoli stosować tylko wskaźniki bezpośrednie, aż do końca całego bloku zawierającego tę dyrektywę. Jeśli jednak zachodzi potrzeba umożliwienia stosowania wskaźników symbolicznych gdzieś w bloku wewnętrznym, użyjemy następnej dyrektywy: no strict 'refs'; Użycie wskaźników na tablice jako wskaźników na asocjacje W wersji 5.005 Perla można używać wskaźnika na tablicę, jakby był to wskaźnik na asocjacje, przynajmniej w pewnym zakresie. Oznacza to, że do elementów tablicy odwołujemy się przez nazwy symboliczne. Jest to nowa, eksperymentalna cecha Perla i należy się liczyć z tym, że w przyszłości może ona ulec zmianie. Aby użyć wskaźnika na tablicę jako wskaźnika na asocjację, trzeba dodać informację o odwzorowaniu pierwszego elementu tablicy, wskazując sposób przygotowania asocjacji. Stosuje się następujący zapis: {klucz1 => indekstablicy1, klucz2 => indekstablicy2, ...} Oto przykład, w którym tworzymy wskaźnik na anonimową tablicę, używając kluczy pierwszy i drugi: $arrayreference = [{pierwszy => l, drugi => 2}, "Hello", "there"]; Teraz możemy odwoływać się do elementów tablicy za pomocą klucza, jak poniżej: $arrayreference = [{pierwszy => l, drugi => 2}, "Hello", "there"]; print $arrayreference->{pierwszy} . " " . $arrayrefarence->{drugi}; Hello there Tworzenie trwałych obszarów zasięgu Obszar to procedura anonimowa mająca dostęp do zmiennych ograniczonych leksy-kalnie, które były w jej zasięgu w chwili kompilacji tej procedury. Procedura utrzymuje te zmienne w swoim zasięgu nawet przy późniejszym wywołaniu. Obszar umożliwia przekazywanie procedurze wartości w chwili jej definiowania tak, aby tę procedurę zainicjalizować. Teraz podamy przykład, który wyjaśni, o co tutaj chodzi. Utworzymy procedurę printem zwracającą wskaźnik na procedurę anonimową. Procedura anonimowa wyświetla przekazany jej tekst oraz tekst przekazany pierwotnie do printem. Przy wywołaniu procedury anonimowej może ona sięgnąć do tekstu przekazanego do printem, mimo że mogłoby się wydawać, że tekst ten znalazł się już poza jej zasięgiem: sub printem { my $stringl = shift; return sub {my $string2 = shift; print "$stringl $string2\n";; } Zwróć uwagę, jak przekażemy słowo „Hello" do zmiennej $stringl w procedurze printem i zapiszemy w $hello wskaźnik na procedurę anonimową: $hello = printem("Hello"); Teraz, nawet jeśli procedura wskazywana przez $hello zostanie wywołana z nowym słowem jako parametrem, $string2, procedura ta — jako że zachowała $stringl w swoim zakresie — może pokazać oba słowa: &$hello("today."); &$hello("there."); Hello today. Hello there. W ten sposób można procedurę rozpocząć dzięki danym —jeszcze przed jej użyciem. Obszarów możemy użyć tylko w odniesieniu do zmiennych ograniczonych leksykalnie. W następnym punkcie powiemy więcej o używaniu obszarów. Tworzenie funkcji na podstawie szablonów Obszarów używamy do utworzenia szablonu funkcji, który umożliwia później tworzenie nowych funkcji i ich dostosowywanie do bieżących potrzeb. Prześledźmy to na przykładzie. Użyjemy jednego szablonu do utworzenia trzech nowych funkcji: printHello, printHi i printGreetings, które będą wyświetlały wyrazy — odpowiednio „Hello", „Hi" i „Greetings". Zaczniemy od zapisania tych słów w tablicy ggreetings: @greetings = ("Hello", "Hi", "Greetings"); Następnie przy użyciu zmiennej leksykalnej zapiszemy pętlę foreach, działającą na tej tablicy (jak już powiedziano w poprzednim punkcie, do tworzenia obszarów można zastosować tylko zmienne ograniczone leksykalnie). W tej pętli utworzymy anonimową funkcję dla każdego elementu tablicy ggreetings, jak również pozycję w tablicy symboli (typ ogólny) danej funkcji: foreach my $term (@greetings) { *{"print" . $term} = sub {print "$term\n”}; } Teraz możemy wywołać nowo utworzone funkcje: printHello (); printGreetings (); Hello Greetings Tak właśnie działają szablony funkcji. Zwróć uwagę, że jeśli wskaźniki do procedur anonimowych zapisalibyśmy jako wskaźniki: @greetings = ("Hello", "Hi", "Greetings"); foreach my $term (@greetings) { ${"print" . $term) = sub {print "$term\n"}; } procedury te trzeba byłoby wywoływać, stosując dereferencję ich wskaźników: &$printHello() ; &$printGreetings(); Hello Greetings Część II Elementy wbudowane Rozdział 9. Wbudowane zmienne Perla W skrócie Perl zawiera wiele zmiennych wbudowanych i nimi właśnie zajmiemy się w tym rozdziale. Niejedną taką zmienną już poznaliśmy, jak choćby naszą ulubienicę $_, zmienną domyślną: while ($_ = <>) { print $_; } Jako że $_ jest zmienną domyślną, powyższy kod jest równoważny następującemu: while (<>) { print; } Rozdział ten jest w znacznej części związany z systemem Unix, gdyż wiele wbudowanych zmiennych ma specyficzny dla tego systemu charakter. Angielskie wersje wbudowanych zmiennych Nazwy wbudowanych zmiennych są dość pokraczne — $] czy $< — ale często istniejs angielskie odpowiedniki tych nazw, których można używać pod warunkiem umieszczenia na początku programu dyrektywy: use English; Użycie tej dyrektywy pozwala stosować angielskie odpowiedniki nazw zmiennych zgodnie z tabelą 9.1. Niektóre zmienne mają więcej niż jeden słowny odpowiednik. Tabela 9.1. Angielskie odpowiedniki zmiennych wbudowanych Zmienna Angielski odpowiednik $’ $POSTMATCH $- $FORMAT_LINES_LEFT $! $OS_ERROR, $ERRNO $” $LIST_SEPARATOR $# $OFMT $$ $PROCESS_ID, $PPID $% $FORMAT_PAGE_NUMBER $& $MATCH $( $REAL_GROUP_ID, $GID $) $EFFECTIVE_GROUP_ID, $EGID $* $MULTILINE_MATCHING $, $OUTPUT_FIELD_SEPARATOR, $OFS $. $INPUT_LINE_NUMBER, $NR $/ $INPUT_RECORD_SEPARATOR, $RS $: $FORMAT_LINE_BREAK_CHARACTERS $; $SUBSCRIPT_SEPARATOR, $SUBSEP $? $CHILD_ERROR $@ $EVAL_ERROR $\ $OUTPUT_RECORD_SEPARATOR, $ORS $] $PERL_VERSION $^ $FORMAT_TOP_NAME $^A $ACCUMULATOR $^D $DEBUGGING $^E $EXTENDED_OS_ERROR $^F $SYSTEM_FD_MAX $^I $INPLACE_EDIT $^L $FORMAT_FORMFEED $^O $OSNAME $^P $PERLDB $^T $BASETIME $^W $WARNING $^X $EXECUTABLE_NAME $_ $ARG $` $PREMATCH $| $OUTPUT_AUTOFLUSH $~ $FORMAT_NAME $+ $LAST_PAREN_MATCH $< $REAL_USER_ID, $UID $= $FORMAT_LINES_PER_PAGE $> $EFFECTIVE_USE_ID, $EUID $0 $PROGRAM_NAME Wiązanie zmiennych wbudowanych z uchwytami plików Wiele zmiennych wbudowanych używa uchwytu aktualnie wybranego pliku (więcej informacji o uchwytach plików znajdziesz w rozdziale 12.), można jednak wskazać inny plik, który ma być używany. W tym celu na początku programu należy wprowadzić dyrektywę: use FileHandle; Po użyciu tej dyrektywy za pomocą różnych metod można ustawiać zmienne związane ze wskazanymi uchwytami plików: metoda UCHWYT WYRAŻENIE To samo można zapisać również w następujący sposób: UCHWYT->metoda (WYRAŻENIE) Metody zestawiono w tabeli 9.2. Niektóre wbudowane zmienne są przeznaczone tylko do odczytu — przy ich omawianiu zostanie to nadmienione. Próba przypisania tym zmiennym wartości spowoduje wygenerowanie przez Perla błędu. Warto też pamiętać, że niektóre zmienne są już przestarzałe (czyli można ich używać, ale nie jest to zalecane) — takie zmienne też zostaną później omówione. Na tym zakończymy nasz wstęp, a dalej zajmiemy się wszystkimi zmiennymi wbudowanymi. Tabela 9.2. Wersje zmiennych wbudowanych, przeznaczone do wiązania z uchwytami plików Zmienna Wersja do obsługi uchwytów plików $- format_lines_left UCHWYT WYRAŻENIE $% format_page_number UCHWYT WYRAŻENIE $, output_field_separator UCHWYT WYRAŻENIE $. input_line_number UCHWYT WYRAŻENIE $/ input_record_separator UCHWYT WYRAŻENIE $: format_line_break_characters UCHWYT WYRAŻENIE $\ output_record_separator UCHWYT WYRAŻENIE $^ format_top_name UCHWYT WYRAŻENIE $^L format_formfeed UCHWYT WYRAŻENIE $| autoflush UCHWYT WYRAŻENIE $~ format_name UCHWYT WYRAŻENIE $= format_lines_per_page UCHWYT WYRAŻENIE Zagadnienia: $' — tekst za wzorcem Zmienna ta zawiera tekst znajdujący się w przeszukiwanym ciągu znaków za dopasowaniem aktualnego wzorca. Oto przykład: $text = 'earlynowlate'; $text =~ /now/; print "Przed wzorcem: \"$`\" Wzorzec: \"$&\" Po wzorcu: \"$'\"\n"; Przed wzorcem: "early" Wzorzec: "now" Po wzorcu: "late" Omawiana zmienna jest przeznaczona tylko do odczytu. $- — numery wierszy w lewej części strony Zmienna $ powoduje, że numery wierszy są umieszczane po lewej stronie bieżącego kanału wyjściowego. $! — ostatni błąd Zmienna $! (użyta w kontekście numerycznym) wskazuje numer ostatniego błędu, natomiast w kontekście tekstowym podaje opis błędu. Oto przykład: use File::Copy; #Próbujemy kopiować nieistniejący plik: copy("nonexistent.pl","new.pl") ; print $!; No such file or directory $" — separator pól wyjściowych interpolowanych wartości z tablic Zmienna ta zachowuje się dokładnie tak samo, jak $, (omówiona dalej w tym rozdziale), czyli określa separator używany przez funkcję print, ale jest stosowana w przypadku wartości wstawianych z tablicy do tekstu. Domyślną wartością tej zmiennej jest spacja. Oto przykład: @array = (l, 2, 3); $" = ','; $text = "@array"; print $text; 1,2,3 $# — format wyjściowy liczb Zmienna $# zawiera format wyjściowy liczb zmiennoprzecinkowych, używany przy ich wyświetlaniu. Spójrz na następujący przykład: $pi = 3.1415926; $# = '%.6g'; print "$pi\n"; 3.14159 Nie zaleca się używania zmiennej $#! $$ — numer procesu Perla Zmienna $$ zawiera numer procesu interpretera Perla wykonującego aktualny skrypt. $% — numer strony wyjściowej Zmienna $% zawiera numer strony wyjściowej w bieżącym kanale wyjścia. $& — ostatnie dopasowanie Zmienna $ & zawiera ostatnie dopasowanie wzorca. Oto przykład: $text = 'earlynowlate'; $text =~ /now/; print "Przed wzorcem: \"$`\" Wzorzec: \"$&\" Po wzorcu: \"$'\"\n"; Przed wzorcem: "early" Wzorzec: "now" Po wzorcu: "late" Zmienna ta jest przeznaczona tylko do odczytu. $( — rzeczywisty GID Zmienna zawiera rzeczywisty GID (identyfikator grupy użytkowników) bieżącego procesu, przy czym wartość ta jest użyteczna praktycznie tylko w systemie Unix. Jeśli na używanym komputerze stosuje się przynależność do wielu grup jednocześnie, $( zawiera całą listę grup. $) — obowiązujący GID Zmienna zawiera obowiązujący aktualnie GID (identyfikator grupy użytkowników) bieżącego procesu, przy czym wartość ta jest użyteczna praktycznie tylko w systemie Unix. Jeśli na używanym komputerze stosuje się przynależność do wielu grup jednocześnie, $) zawiera całą listę grup. Czym różni się GID obowiązujący od GID rzeczywistego? Wprawdzie program może zostać uruchomiony przez kogoś należącego do innej grupy, ale można go uruchomić z uprawnieniami wynikającymi z jego własnej grupy. Właśnie ta własna grupa jest w takiej sytuacji grupą obowiązującą. $* — wzorce wielowierszowe Ustawienie $* pozwala zastosować wzorce wielowierszowe do tekstu zawierającego znaki nowego wiersza, które wskazuje się za pomocą symboli ^ i $. Jeśli $* ma wartość l, $ i ^ odpowiadać będą odpowiednio miejscu przed znakiem i po znaku nowego wiersza (wartość domyślna to 0). Oto przykład: $text = "Oto \nnasz\ntekst.' $text =~ /^nasz/; print $&; # nie znaleziono $* = 1; $text =~ /^tekst/; print $&; tekst Nie zaleca się używać zmiennej $*, a zamiast niej można stosować modyfikatory s i m. $, — separator pól wyjściowych Zmienna $, zawiera separator pól wyjściowych dla operatora print. Oto przykład pokazujący sposób jej użycia: $, = ';'; print l, 2, 3; 1;2;3 $. — numer wiersza wejściowego Zmienna $. zawiera bieżący numer wiersza wejściowego uchwytu pliku, z którego ostatnio realizowano odczyt. $/ — separator rekordów wejściowych Zmienna $/ to separator rekordów wejściowych — domyślnie zmienna ta zawiera znak nowego wiersza. Przy odczytywaniu z pliku rekordów jest stosowany właśnie separator z tej zmiennej. Oto ciekawy przykład: zwykle odczytuje się jednorazowo jeden wiersz z pliku, jeśli jednak usuniemy definicję $/, można od razu odczytać wszystkie wiersze z pliku: undef $/; open HANDLE, "file.txt"; $text = ; print $text; Tutaj znajduje się tekst z pliku. $: — znaki dzielenia wiersza Zmienna $: zawiera zestaw znaków przeznaczonych do łamania wierszy na wyjściu. Po tych znakach Perl może podzielić tekst, aby wypełnić dalsze pola formatu. $; — separator indeksów Zmienna $; umożliwia emulowanie tablic wielowymiarowych za pomocą asocjacji. Zmienna ta zawiera tekst rozdzielający indeksy, który zostanie użyty przy przekazywaniu do asocjacji indeksów pseudotablic. Indeksy tablic można symulować za pomocą klucza asocjacji rozdzielającego indeksy przecinkami. Przyjrzyjmy się dwóm poniższym równoważnym wyrażeniom: $hash{x,y,z} $hash{join($;, x, y, z)} Oto przykład użycia $;. Traktujemy %hash jako tablicę i wprowadzamy tekst do elementu l, l, l: $hash("1$;1$;1"} = "Hello!"; print $hash{l,l,l}; Hello! Zagadnienie związane: Deklarowanie tablic tablic $? — status ostatniego zamknięcia potoku, wywołania polecenia przez * lub wywołania funkcji systemowej Zmienna $? zawiera status (być może kod błędu) zwrócony przez ostatnie zamknięcie potoku, polecenie ujęte znakami " (na przykład " uptime') lub funkcję systemową. $@ — błąd ostatniej funkcji eval Zmienna $@ zawiera komunikat błędu składniowego Perla ostatniej instrukcji eval (jeśli błąd nie wystąpił, wartościąjest znak pusty). $[ — początek indeksów tablic Zmienna $[ zawiera najmniejszą wartość indeksu tablicy. Domyślnie jest to 0, ale można wartość tę zmienić na przykład na l. Nie zaleca się używania zmiennej $[ ! $\ — separator rekordów wyjściowych Zmienna $\ zawiera separator rekordów wyjściowych, używany przez operator print. Zwykle jest to ciąg pusty, ale można też użyć jakiegoś tekstu: $\ = "KONIEC_DANYCH" ; print "Hello!"; Hello!KONIEC_DANYCH $] — wersja Perla Zmienna ta zawiera numer wersji używanego interpretera Perla, na przykład: print $]; 5.00502 $^ — format nagłówka strony Zmienna ta zawiera obowiązujący format nagłówka strony kanału wyjściowego. $^A — zapis bufora Zmienna ta zawiera bieżącą wartość bufora zapisu (po wywołaniu instrukcja write wypisuje całą zawartość bufora). $^D — flagi debugowania Zmienna $^D zawiera obowiązujące ustawienia debugowania. Zagadnienie związane: Przechwytywanie błędów wykonania $^E — informacje o błędzie związane z systemem operacyjnym Zmienna $^E zawiera informacje o błędzie, charakterystyczne dla używanego systemu operacyjnego. Obecnie $^E ma takie samo znaczenie jak $!, nie dotyczy to jedynie systemów VMS, OS/2, Win32 i MacPerl. Oto przykład pokazujący różne komunikaty o błędach Perla i Windows: use File::Copy; #Próbujemy kopiować nieistniejący plik: copy("nonexistent.pl","new.pl") ; print "$!\n"; print "$^E\n"; No such file or directory The system cannot find the file specified $^F — największy systemowy deskryptor pliku Zmienna $^F zawiera największy deskryptor pliku systemu Unix (zwykle 2). $^H — ustawienia kontroli składniowej Zmienna zawiera zestaw obowiązujących kontroli składni, ustawionych przez use strict i inne dyrektywy. $^I — wartość edycji w miejscu Zawiera wartość rozszerzenia edycji w miejscu. Właściwość tę można wyłączyć, wykonując instrukcję undef na zmiennej $^I. $^L — zmiana strony wyjściowej Zmienna ta zawiera znak używany przez Perla do zmiany strony na następną — wartością domyślną jest \f. $^M — awaryjny bufor pamięci W Perlu brak pamięci jest błędem, którego nie można przechwycić, ale jeśli stosowana wersja Perla została skompilowana tak, aby to umożliwić, Perl może użyć zawartości zmiennej $^M jako bufora awaryjnego. Jeśli na przykład używana wersja została skompilowana z przełącznikiem –DPERL_EMERGENCY_SBR, bufor awaryjny o rozmiarze l MB można zarezerwować w sposób następujący: $^M = ‘ ‘ x (2 ** 20); $^O — nazwa systemu operacyjnego Zmienna $^O zawiera nazwę systemu operacyjnego, dla którego używana wersja Perla została skompilowana. W systemach uniksowych, w których pakiet Perla można tworzyć samodzielnie, zmienna ta często zawiera tekst nie mający nic wspólnego z systemem operacyjnym, ale zawierający lokalną nazwę systemu. Gotowe już dystrybucje Perla są pod tym względem znacznie bardziej wiarygodne. Spójrzmy na poniższy przykład dotyczący Windows: print $^O; MSWin32 $^P — obsługa debugowania Zmienna $^P zawiera wewnętrzną konfigurację dotyczącą debugowania. Oto znaczenie poszczególnych bitów: Bit 0 — ustawiany w celu debugowania wejść i wyjść z procedur, Bit l — umożliwia wykonywanie programu w debuggerze wiersz po wierszu, Bit 2 — wyłącza optymalizacje, co upraszcza usuwanie błędów, Bit 3 — zachowuje dane do interaktywnego ich badania, Bit 4 — zachowuje informacje o wierszach źródłowych, w których zdefiniowano procedurę, Bit 5 — rozpoczyna sesję w trybie wykonywania kolejnych kroków. $^R — wynik asercji ostatniego wyrażenia regularnego Zmienna $^R zawiera wynik udanej ewaluacji asercji ostatniego wyrażenia regularnego. Oto przykład, w którym używamy asercji o zerowej długości (?{}) do wykonania kodu Perla wewnątrz wyrażenia regularnego, a następnie za pomocą zmiennej $^R wyświetlamy wyniki wykonania tego kodu: $text = "text"; Stext =~ /x(?{$variablel = 5})/; print $^R; 5 $^S — stan interpretera Zmienna ta zawiera aktualny stan interpretera Perla. Wartość ta jest prawdą, jeśli wykonywana jest instrukcja eval, a fałszem — w przeciwnej sytuacji. $^T — czas uruchomienia skryptu Zmienna zawiera czas, kiedy skrypt został uruchomiony, mierzony w sekundach od początku roku 1970 (jest to standardowy sposób mierzenia czasu w systemie Unix). Zobaczmy przykład: print $^T; 909178645 $^W — ustawienie przełącznika ostrzeżeń Zmienna ta zawiera obowiązujące ustawienie przełącznika -w — wartość prawdy lub fałszu. Aby ustawić tę zmienną za pomocą przełącznika, trzeba Perla wywołać następująco: %per!5 -w warn.pl Wtedy w kodzie uzyskamy: print $^W; 1 $^X — nazwa pliku interpretera Zmienna ta zawiera nazwę samego Perla, a wynik może wyglądać następująco: print $^X; /usr/bin/perl5 $_ — zmienna domyślna $_ to zmienna domyślna Perla. Wiele operatorów i funkcji używa właśnie tej zmiennej, jeśli nie zostanie podana inna. Na przykład pętla while i operator print w poniższym kodzie używają zmiennej $_, zatem kod: while ($_ = <>) { print $_; } działa tak samo jak: while (<>) { print; } Jak już niejednokrotnie w tej książce mówiono, wiele innych operatorów (jak s/// czy tr///) oraz funkcji (jak chop czy chomp) używa danych ze zmiennej $_, o ile nie zostanie wskazana inna lokalizacja. $` — tekst przed wzorcem Zmienna ta zawiera tekst znajdujący się w przeszukiwanym ciągu znaków przed dopali owaniem aktualnego wzorca. Oto przykład: $text = 'earlynowlate'; $text =~ /now/; print "Przed wzorcem: \"$'\" Wzorzec: \"$&\" Po wzorcu: \"$'\"\n"; Przed wzorcem: "early" Wzorzec: "now" Po wzorcu: "late" $I — czyszczenie bufora wyjściowego Kiedy zmienna $I ma wartość true, Perl usuwa dane wyjściowe przy każdym poleceniu pisania lub drukowania do bieżącego kanału wyjścia. Omawianą zmienną zwykle ustawia się w przypadku korzystania z potoków, jak poniższej, gdzie użyto metody autoflush (więcej informacji o tej metodzie znajdziesz w tabeli 9.2): pipe(READER, WRITER); autoflush WRITER 1; Poniższy kod zadziała tak samo: pipe(READER, WRITER); WRITER->autoflush(1); $~ — nazwa formatu raportowania Zmienna ta zawiera nazwę obowiązującego formatu raportowania bieżącego kanału wyjściowego. $+ — ostatnie dopasowanie nawiasów Zmienna $+ zawiera ostatnie dopasowanie wzorca ujętego w nawiasy. Spójrz na następujący przykład: $text = "Here is the text."; $text =~ /(\w+) is the (\w+)./; print $+; text Zmienna ta jest przeznaczona tylko do odczytu. $< — rzeczywisty UID Zmienna $< zawiera rzeczywisty UID (identyfikator użytkownika) bieżącego procesu, co jest przydatne w zasadzie tylko w systemie Unix. Oto przykład: print $<; 166 $= — obowiązująca długość strony Zmienna $= zawiera długość strony kanału wyjściowego w wierszach (wartość domyślna to 60). Zobacz poniższy przykład: print $=; 60 $> — obowiązujący UID Zmienna $> zawiera obowiązujący aktualnie UID (identyfikator użytkownika) bieżącego procesu. Zmienna ta jest przydatna w zasadzie tylko w systemie Unix. Oto przykład: print $>; 166 $0 — nazwa programu Zmienna $0 zawiera nazwę wykonywanego właśnie skryptu Perla, na przykład: print $0; script.pl $ARGV — nazwa bieżącego pliku Zmienna $argv zawiera nazwę bieżącego pliku, odczytywanego przez o. Na przykład skrypt można uruchomić następująco: %perl read.pl file.txt W tym przypadku $argv zawiera nazwę pliku przekazanego skryptowi w wierszu poleceń: $text = <>; print $ARGV; file.txt $n — dopasowanie numer n Zmienne $n zawierają dopasowania wzorca odpowiadającego parze nawiasów numer n (o nawiasach dowiesz się więcej w rozdziale 6.). Oto przykład, w którym zmieniamy kolejność słów w tekście: $text = "nie i tak"; $text =~ s/(\w+) (\w+) (\w+)/$3 $2 $!/; print $text; tak i nie Zmienne $l, $2 — i tak dalej — są tylko do odczytu. Zagadnienie związane: Odwołania do poprzednich dopasowań %ENV —-ustawienia środowiska Asocjacja %ENV zawiera ustawienia zmiennych środowiskowych, przy czym zestaw kluczy jest zależny od stosowanego systemu operacyjnego. Zobacz, jakiego typu wartości można oczekiwać w systemie Unix: while(($key, $value) = each(%ENV)) { print "$key => $value\n"; } SHELL => /bin/csh HOME => /home/username %INC — pliki włączane Asocjacja %INC zawiera jedną pozycję, która opisuje każdy plik włączony instrukcjami do i reąuire. Kluczem jest nazwa pliku, wartością położenie pliku (zresztą tej właśnie asocjacji używa sam Perl do sprawdzenia, czy dany plik był już włączony). %SIG — obsługa sygnałów Asocjacji %SIG można użyć do określania procedur obsługi sygnałów, na przykład zgłaszanie błędów wyłącza się następująco: local $SIG{_WARN _) - sub{}; @_ — parametry procedur Parametry przekazywane procedurom są umieszczane w tablicy @_ i stamtąd można je pobierać, jak w poniższym przykładzie: sub addem ( $valuel = shift@_; $value2 = shift@_; print "$valuel + $value2 = " . ($valuel + $value2) . "\n"; } addem(2, 2); 2 + 2 = 4 Zagadnienie związane: Odczytywanie parametrów procedury @ARGV — parametry wiersza poleceń Tablica @ARGV zawiera parametry wiersza poleceń przekazane skryptowi. Załóżmy na przykład, że wywołanie ma taką postać: %perl script.pl a b c d Jeśli skrypt wydrukuje elementy @ARGV, wynik będzie następujący: print join(", ", @ARGV); a, b, c, d Pierwszy parametr przekazany skryptowi to $ARGV[0], zatem $#ARGV to liczba parametrów pomniejszona o jeden. @INC — położenie uruchamianych skryptów Tablica @INC zawiera listę miejsc, w których Perl ma szukać skryptów uruchamianych poleceniami do, require i use, na przykład: print join(', ', @INC); /usr/local/lib/perl5/sun/5.00502, /usr/local/lib/perl5, /usr/local/lib/perl5/site_perl/sun, /usr/local/lib/perl5/site_perl, Rozdział 10. Wbudowane funkcje: przetwarzanie danych W skrócie Funkcje wbudowane Perl standardowo zawiera szereg funkcji wbudowanych, z których wielu już używaliśmy — na przykład funkcji push umieszczającej w tablicy nowe wartości: push(@array, "raz"); push(@array, "dwa"); push(@array, "trzy"); print $array[0]; raz W tym rozdziale przyjrzymy się funkcjom Perla, przeznaczonym do przetwarzania i obsługi danych (w tym funkcjom używanym do obsługi tekstu, sortowania danych), a także matematycznym, obsługi tablic, asocjacji i innym. W następnym rozdziale omówimy wbudowane funkcje obsługi wejścia-wyjścia oraz komunikacji między procesami. Funkcje omawiane w tym rozdziale — poza grupą funkcji POSIX (Przenośny interfejs dostępu do systemu operacyjnego) — są wbudowane bezpośrednio w samego Perla, zatem można ich użyć w dowolnej chwili bez jakichkolwiek dodatkowych przygotowań. Wiele z nich nieraz już stosowaliśmy, zatem nie wymagają one dodatkowych wstępów — od razu przejdziemy do szczegółów. Zagadnienia: abs — wartość bezwzględna Funkcja ta zwraca wartość bezwzględną liczby podanej jako parametr (jeśli parametr nie zostanie podany, funkcja użyje wartości ze zmiennej domyślnej $_): abs WARTOŚĆ abs Oto przykład: print abs -5; 5 atan2 — arcus tangens Funkcja atan2 zwraca arcus tangens stosunku Y/x (zwracana wartość należy do zakresu od -pi do pi): atan2 Y, X Nie istnieje natomiast w Perlu funkcja tan (jest jednak dostępna w pakiecie POSIX jako POSIK::tan. Zajrzyj do punktu „POSDC (grupa funkcji)" w dalszej części tego rozdziału). Wartość tangensa można też oczywiście uzyskać, dzieląc sinus przez cosinus. chomp — usunięcie końców wierszy Funkcja chomp usuwa z tekstu lub tekstów końce wierszy. Niepodanie parametrów funkcji chomp powoduje użycie wartości zmiennej $_: chomp ZMIENNA chomp LISTA chomp Funkcja chomp zwraca liczbę znaków usuniętych ze wszystkich przekazanych parametrów. W przypadku użycia tej funkcji na liście usuwane są końce wszystkich elementów listy, ale zwracana wartość dotyczy tylko ostatniego elementu. Omawianej funkcji zwykle używa się do usuwania znaków końca wiersza ze strumienia wejściowego, na przykład: while (<>) { chomp; print; } Do określenia, co jest zakończeniem wiersza, funkcja chomp wykorzystuje zmienną $/. chop — usunięcie ostatniego znaku Funkcja chop usuwa z tekstu lub tekstów ostatni znak i go zwraca. Jeśli niepodane zostaną parametry tej funkcji, chop użyje wartości zmiennej $_: chop ZMIENNA chop LISTA chop Jeśli funkcja zostanie uruchomiona na liście, jest ona wykonywana na wszystkich elementach tej listy, ale zwracana wartość dotyczy ostatniego elementu. Oto przykład: while (<>) { chop ; print; } Przyjmuje się, że zamiast chop bezpieczniej jest używać funkcji chomp, gdyż stosuje ona jedynie znaki końca wiersza. chr — znak o podanym kodzie Funkcja chr zwraca znak odpowiadający podanemu kodowi ASCII. Brak parametru powoduje użycie zmiennej $_: chr LICZBA chr Oto przykład: print chr 65; A cos — cosinus Funkcja cos zwraca wartość podanego w radianach kąta (dwa pi radianów to kąt pełny, 360°). Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_. cos WYRAŻENIE cos Aby uzyskać wartość arcus cosinusa, należy zastosować funkcję POSIX::acos (zajrzyj do punktu „POSIX (grupa funkcji)" w dalszej części tego rozdziału). each — pary kluczy i wartości asocjacji W kontekście list funkcja each zwraca pary klucz+wartość (jako listy) asocjacji. W kontekście skalarnym each zwraca klucze kolejnych elementów asocjacji: each ASOCJACJA Oto przykład: $hash{kanapka) = 'grzanka'; $hash{picie) = 'piwo korzenne'; while(($key, $value) = each(%hash)) {print "$key => $value\n";} picie => piwo korzenne kanapka => grzanka eval — uruchamianie kodu Perla Funkcji eval można użyć do interpretowania i wykonywania kodu Perla: eval WYRAŻENIE eval BLOK eval Wartość zwrócona przez wyrażenie jest analizowana i uruchamiana jako kod Perla w chwili wywołania eval. Jeśli kod zostanie przekazany jako blok, jest analizowany tylko raz (wtedy, kiedy analizowana jest sama funkcja eval). W sytuacji, gdy nie zostanie podane ani wyrażenie, ani blok, eval używa zawartości zmiennej $_. Oto przykład zastosowania funkcji eval: eval {print "Hello "; print "there.";); Hello there. W przypadku pojawienia się błędu wykonania jest on zwracany w zmiennej $@. exists — sprawdzanie istnienia klucza w asocjacji Funkcja exists zwraca wartość prawdy (true), jeśli podany klucz istnieje w asocjacji: exists WYRAŻENIE exp — potęga liczby e Funkcja exp zwraca liczbę e podniesioną do potęgi określonej WYRAŻENIEM (jeśli wyrażenie nie zostanie podane, funkcja użyje wartości zmiennej $_): exp WYRAŻENIE exp Oto przykład: print exp 1; 2.71828182845905 hex — konwersja na liczbę szesnastkową Funkcja hex zwraca wartość zapisaną szesnastkowo. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: hex WYRAŻENIE hex Zobacz przykład: print hex "10"; 16 index — położenie części tekstu Funkcja index zwraca położenie podnapisu w napisie, przy czym początek nie może być bliżej niż POZYCJA0. Jeśli POZYCJA0 nie zostanie podana, funkcja wyszukiwanie zacznie od początku napisu: index NAPIS, PODNAPIS, POZYCJA index NAPIS, PODNAPIS Jeśli podnapis nie zostanie odnaleziony, index zwróci -l (a dokładnie: zwróci liczbę mniejszą niż pierwszy indeks w tablicy, zwykle 0). Oto przykład: $text = "Herę's the text!"; print index $ text, 'text'; 11 int — odrzucenie części ułamkowej Funkcja int zwraca część całkowitą wyrażenia. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: int WYRAŻENIE int Funkcja int po prostu odrzuca część ułamkową liczby i zwraca część całkowitą, zatem funkcji tej nie należy używać do zaokrąglania wartości (do tego można zastosować sprintf, printf lub funkcje POSIX::floor i POSIX::ceil — więcej informacji o dwóch ostatnich znajdziesz w punkcie „POSIX (grupa funkcji)", dalej w tym rozdziale). Oto przykład zastosowania funkcji int: print int 1.999; l join - złączenie elementów listy w ciąg znaków Funkcja join łączy elementy listy w pojedynczy ciąg znaków, pola są rozdzielane wyrażeniem: join WYRAŻENIE, LISTA Spójrz na poniższy przykład: garray = (l, 2, 3, 4, 5, 6, 7, 8, 9, 10); print join(", ", @array); l, 2, 3, 4, 5, 6, 7, 8, 9, 10 keys — klucze asocjacji W kontekście list funkcja keys zwraca listę wszystkich kluczy danej asocjacji. Z kolei w kontekście skalarnym zwraca liczbę kluczy: keys ASOCJACJA Oto przykład: $hash{kanapka) = salami; $hash(picie} = 'piwo korzenne'; foreach $key (keys %hash) {print $hash{$key}. "\n";} piwo korzenne salami lc — zmiana na małe litery Funkcja Ic zwraca przekazany jej tekst, zapisany małymi literami. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: lc WYRAŻENIE lc Oto przykład: print lc 'HELLO!' ; hello! lcfirst — zmiana pierwszego znaku na małą literę Funkcja lcfirst zwraca przekazany jej tekst, podmieniając pierwszą literę na małą. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: lcfirst WYRAŻENIE lcfirst length — długość tekstu Funkcja length zwraca długość wyrażenia w bajtach. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: length WYRAŻENIE length Oto przykład: $text = "Here is the text."; print length $text; 17 log — logarytm naturalny Funkcja log zwraca logarytm naturalny wyrażenia (czyli logarytm o podstawie e). Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: log WYRAŻENIE log map — wykonanie kodu dla każdego elementu Funkcja map wykonuje blok lub wyrażenie dla każdego elementu listy: map BLOK, LISTA map WYRAŻENIE, LISTA Poniżej znajduje się przykład, w którym używamy funkcji uc do zamiany wszystkich elementów tablicy na wielkie litery: @array = (a, b, c, d, e, f); @array = map(uc, @array); print join(", ", @array); A, B, C, D, E, F oct — konwersja wartości ósemkowych Funkcja oct przekształca podaną wartość ósemkową na dziesiętną. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: oct WYRAŻENIE oct Oto przykład: print oct 10; 8 ord — kod ASCII Funkcja ord zwraca kod ASCII pierwszego znaku wyra'żenia. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: ord WYRAŻENIE ord Oto przykład: print ord ‘A’; 65 pack — kompresja danych Funkcja pack pobiera listę wartości i zbiera je w strukturę binarną: pack SZABLON, LISTA SZABLON to ciąg znaków określających kolejność i typy wartości, przy czym do dyspozycji są następujące określenia formatu: @ — wypełnienie wartościami null na podanej pozycji bezwzględnej, A — ciąg ASCII uzupełniany spacjami, a —ciąg ASCII, b — ciąg bitów (porządek rosnący), B — ciąg bitów (porządek malejący), c — wartość znakowa ze znakiem, C — wartość znakowa ze znakiem, d — liczba zmiennoprzecinkowa podwójnej precyzji w formacie lokalnym, f— liczba zmiennoprzecinkowa pojedynczej precyzji w formacie lokalnym, H — ciąg szesnastkowy (najpierw bity najbardziej znaczące), h — ciąg szesnastkowy (najpierw bity najmniej znaczące), i — liczba całkowita ze znakiem, I — liczba całkowita bez znaku, l — długa liczba całkowita ze znakiem, L — długa liczba całkowita bez znaku, N — długa liczba całkowita z ważniejszymi bajtami na początku, n — krótka liczba całkowita z ważniejszymi bajtami na początku, p — wskaźnik na znaki ASCIIZ (zakończony wartościąnull), P — wskaźnik na strukturę, s — krótka liczba całkowita ze znakiem, S — krótka liczba całkowita bez znaku, u — tekst poddany kodowaniu uuencode, V — długa liczba całkowita z ważniejszymi bajtami na końcu, v — krótka liczba całkowita z ważniejszymi bajtami na końcu, w — liczba całkowita zakodowana za pomocą BER (ISO Basic Encoding Rules), x — bajt zerowy (null), X — bajt rezerwowy. Za każdą z liter może znajdować się liczba określająca liczbę powtórzeń, można też użyć znaku * jako oznaczenia dowolnej liczby powtórzeń, na przykład: print pack("ccc", 88, 89, 90); XYZ print pack("c3", 65, 66, 67); ABC print pack("c*", 68, 69, 70, 71); DEFG pop — odebranie z tablicy elementu Funkcja pop zwraca ostatni element tablicy, skracając przy tym tablicę o ten element. Jeśli tablica nie zostanie wskazana, pop zostanie użyta @_: pop TABLICA pop Oto przykład — zauważ, że w pierwszym wierszu, jeśli @array nie istnieje, jest ona tworzona: push $array, 5; print pop @array; 5 POSIX (grupa funkcji) Narodowy Instytut Standaryzacji i Technologii i Laboratorium Systemów Komputerowych (NIST/CSL) wraz z innymi firmami i organizacjami stworzyły standard POSIX (Portable Operating System Interface, Przenośny interfejs systemu operacyjnego). POSIX to duża biblioteka funkcji zapisanych w stylu C, obejmująca typowe operacje programistyczne: od podstawowych funkcji matematycznych po złożoną obsługę plików. Moduł POSIX Perla daje dostęp do prawie wszystkich identyfikatorów POSIX 1003.1 — łącznie około 250 funkcji. Funkcje te nie należą do Perla tak, jak pozostałe funkcje omawiane w tym rozdziale, ale jako że POSIX oferuje programistom znacznie więcej możliwości, wspominamy tu o nim. Moduł POSIX do programu dodaje się za pomocą instrukcji use: use POSIX; # Dodanie całej biblioteki POSIX use POSIX qw(FUNKCJA); # Dodanie wybranej funkcji Oto przykład użycia funkcji tangens dla wartości pi/4 — korzystamy z funkcji tan pakietu POSIX, która nie ma odpowiednika w Perlu. Sposób uzyskania wartości pi/4 opisano przy omawianiu funkcji atan2: use POSIX; print POSIX::tan(atan2 (1, 1)); 1 push — wstawienie wartości do tablicy Funkcja push dodaje na koniec tablicy wartość lub wartości, zwiększając przy tym wielkość elementów tablicy o liczbę dodanych wartości: push TABLICA, LISTA Oto przykład: push @ array, 5; print pop @array; 1 rand — liczba losowa Funkcja rand zwraca liczbę losową między O a wartością podanego wyrażenia dodatniego. Jeśli żadne wyrażenie nie zostanie podane, rand jako górnego ograniczenia użyje l: rand WYRAŻENIE rand Funkcja ta automatycznie wywołuje s rand (chyba że ta ostatnia została uprzednio już wywołana), aby zainicjować generator liczb losowych — funkcja s rand jest opisana w dalszej części tego rozdziału. Oto przykład użycia rand: print rand; 0.418304443359375 reverse — odwrócenie kolejności elementów listy Funkcja reverse odwraca kolejność elementów listy: reverse LISTA Oto przykład jej zastosowania: @array = (l, 2, 3); print join(", ", reverse @array); 3, 2, 1 rindex — funkcja odwrotna do index Funkcja rindex działa w zasadzie tak samo jak index, ale zwraca położenie ostatniego, a nie pierwszego wystąpienia PODNAPISU w NAPISIE: rindex NAPIS, PODNAPIS, POZYCJA rindex NAPIS, PODNAPIS Jeśli podana zostanie POZYCJA, rindex zwróci ostatnie wystąpienie znajdujące się na POZYCJI lub przed nią. scalar — wymuszenie kontekstu skalarnego Funkcja scalar wymusza potraktowanie wyrażenia w kontekście skalarnym: scalar WYRAŻENIE Nie istnieje analogiczna funkcja, która wymuszałaby kontekst list. Oto przykład użycia funkcji scalar; zwróć uwagę, że zostaje pokazany ostatni element listy: @array = (l, 2, 3); print scalar @array; 3 shift — przesunięcie wartości w tablicy Funkcja shift pobiera z tablicy pierwszą wartość i ją zwraca, zaś wszystkie pozostałe elementy tablicy przesuwa do przodu, skracając przy tym tablicę o pobrany element: shift TABLICA shift Jeśli nie zostanie podana tablica, funkcja używa @_ w procedurach i formatach, ale w zasięgu pliku lub zasięgu leksykalnym eval, BEGIN, END i INIT używa @ARGV. sm — sinus Funkcja sin zwraca sinus podanego wyrażenia. Jeśli nie zostanie podany parametr, funkcja użyje wartości zmiennej $_: sin WYRAŻENIE sin Aby uzyskać arcus sinus, należy zastosować funkcję POSIX::asin (zajrzyj do punktu „POSIX (grupa funkcji)", wcześniej w tym rozdziale). sort — sortowanie listy Funkcja sort sortuje listę i zwraca wynik tej operacji: sort PROCEDURA LISTA sort BLOK LISTA sort LISTA Jeśli nie zostanie podana procedura ani blok, sortowanie odbywa się w standardowym porządku alfabetycznym. W sytuacji, gdy podana zostanie procedura, musi ona zwracać liczbę całkowitą mniejszą, równą zeru lub od niego większą, wskazując na sposób uporządkowania przekazanych wartości. Można też podać BLOK jako pewnego rodzaju procedurę wywoływaną. Oto przykłady użycia sort: @array = ('z’, 'b’, 'a', 'x', 'y’, ‘c’); print join („, „, @array) . “\n”; a, b, c, x, y, z print join("sort, ", sort {$a cmp $b} @array) . "\n"; a, b, c, x, y, z print join(", ", sort {$b cmp $a} @array) . "\n"; z, y, x, c, b, a @array = (l, 5, 6, 7, 3, 2); print join(", ", sort ($a <=> $b} @array) . "\n"; l, 2, 3, 4, 5, 6, 7 print join(", ", sort {$b <=> $a} @array) . "\n"; 7, 6, 5, 4, 3, 2, l Zagadnienie związane: Użycie operatorów równości splice —-warstwy tablic Funkcja splice usuwa 2. tablicy elementy wskazane przez OFFSET i ILOŚĆ, zastępując je elementami listy, o ile lista zostanie podana: splice TABLICA, OFFSET, ILOŚĆ, LISTA splice TABLICA, OFFSET, ILOŚĆ splice TABLICA, OFFSET W kontekście list splice zwraca elementy usunięte z tablicy, w kontekście skalarnym zwraca ostatni usunięty element (lub wartość undef, jeśli nie usunięto żadnych elementów). W przypadku niepodania ILOŚCI, splice usuwa wszystko od OFFSET do końca tablicy. Oto przykład, w którym wstawiamy nowy element trzy do tablicy już zawierającej elementy jeden i dwa: @array = "jeden", "dwa"); splice(@array, 2, 0, "trzy"); print join(", ", @array); jeden, dwa, trzy Zagadnienie związane: Warstwy tablic split — rozbicie tekstu na tablicę znaków Funkcja split rozbija tekst na tablicę wyrazów: split /WZORZEC/, WYRAŻENIE, LIMIT split /WZORZEC/, WYRAŻENIE split /WZORZEC/ split Jeśli wzorzec zostanie podany, Perl traktuje wszystko, co do niego pasuje, jako ogranicznik pól w tekście. W sytuacji, gdy podany zostanie limit, split rozdzieli nie więcej niż LIMIT pól. Oto przykład: print join('-', split(//, 'Hello')); H-e-1-l-o sprintf — formatowanie tekstu Funkcja sprintf formatuje tekst, podstawiając do niego listę wartości: sprintf FORMAT, LISTA Każdemu elementowi listy odpowiada jedna konwersja w formacie. Dostępne są następujące konwersje: %% — znak procentu, %c — znak o podanym kodzie, %d — liczba całkowita ze znakiem, zapisana dziesiętnie, %e — liczba zmiennoprzecinkowa w notacji naukowej, %E —jak %e, ale stosowana jest wielka litera E, %f — liczba zmiennoprzecinkowa w dziesiętnym zapisie, %g — liczba zmiennoprzecinkowa w zapisie zgodnym z %e lub %f, %G —jak %g, ale ewentualnie jest używane wielkie G, %n — wskazana liczba znaków wyjściowych jest zapisywana w następnej zmiennej, %o — liczba całkowita bez znaku, zapisana ósemkowo, %p — wskaźnik (zapisany szesnastkowo adres), %s — tekst, %u — całkowita liczba bez znaku, zapisana dziesiętnie, %x — całkowita liczba bez znaku, zapisana szesnastkowo, %X—jak %x, ale są używane wielkie litery. W celu zapewnienia zgodności z poprzednimi wersjami Perl udostępnia jeszcze kilka dodatkowych konwersji: %D — równoważne % l d, %F — równoważne % l f, %i — równoważne %d, %O — równoważne % l o, %U — równoważne %lu. Oprócz tego Perl umożliwia wstawianie flag między znakiem % a literą konwersji: - — wyrównanie pola do lewej; # — poprzedzenie niezerowej liczby ósemkowej wyrażeniem „0", zaś niezerowej szesnastkowej wyrażeniem „0x"; .liczba "skala": — w przypadku liczb zmiennoprzecinkowych liczba cyfr po kropce dziesiętnej, maksymalna długość tekstu lub minimalna długość liczb całkowitych; + — poprzedzenie liczby dodatniej symbolem +; 0 — w celu wyrównania do prawej nie są używane spacje, lecz zera; h — liczba całkowita interpretowana jest jako typ short lub unsigned short języka C; l — liczba całkowita interpretowana jest jako typ long lub unsigned long jeżyka C; liczba — minimalna szerokość pola; spacja — poprzedzenie liczby dodatniej spacją. I jest w końcu specyficzna flaga języka Perl: V — liczba całkowita interpretowana jest jako standardowy typ całkowity Perla. Oto kilka przykładów (zwróć uwagę na zaokrąglenie w pierwszym z nich): $value = 1234.56789; print sprintf "%.5f\n", $value 1234.5679 printf sprintf "%.5f\n", $value; 1234.56789 printf sprintf "%6.6f\n", $value; 1234.567890 printf sprintf "%+.4e\n", $value; +1.234e+003 sqrt — pierwiastek kwadratowy Funkcja sqrt zwraca pierwiastek kwadratowy przekazanego jej wyrażenia. Jeśli wyrażenie zostanie pominięte, funkcja używa wartości zmiennej $_: sqrt WYRAŻENIE sqrt Oto przykład: print sqrt 144; 12 srand — wartość inicjalizująca generatora liczb losowych Funkcja srand inicjalizuje generator liczb losowych, rand. Jeśli wyrażenie zostanie pominięte, srand użyje wartości wyliczonej na podstawie aktualnego czasu i identyfikatora procesu: srand WYRAŻENIE srand substr — zwraca część tekstu Funkcja substr zwraca część przekazanego jej tekstu: substr WYRAŻENIE, OFFSET, DŁUGOŚĆ, NOWY substr WYRAŻENIE, OFFSET, DŁUGOŚĆ substr WYRAŻENIE, OFFSET Pierwszy znak zwracanego podciągu znajduje się na pozycji OFFSET. Jeśli offset jest wartością ujemną, substr zaczyna zliczanie od końca tekstu i porusza się wstecz. Jeżeli nie podano długości, substr zwraca cały tekst do końca tekstu wejściowego. W przypadku, gdy DŁUGOŚĆ jest ujemna, substr pomija podaną liczbę znaków z końca. Podając nowy tekst, można podstawić część tekstu. Oto kilka przykładów: $text = "Here is the text."; print substr ($text, 12) . "\n"; text. . print substr ($text, 12, 4} . "\n"; text substr ($text, 12, 4, "word"); print "$text\n"; Here is the word. time — sekundy od l stycznia 1970 roku Funkcja time zwraca liczbę sekund od chwili rozpoczęcia się epoki: time W większości wersji Perla za początek epoki przyjmuje się godzinę 00:00:00 dnia l stycznia 1970 roku (choć na MacOS jest to godzina 00:00:00 dnia l stycznia 1904 roku). uc — wielkie litery Funkcja uc zwraca przekazane jej słowo po zmianie wszystkich jego małych liter na wielkie. Jeśli wyrażenie zostanie pominięte, funkcja używa wartości zmiennej $_: uc WYRAŻENIE uc Oto przykład: print uc 'hello!’; HELLO! ucfirst — zmiana pierwszej litery na wielką Funkcja ucfirst zwraca tekst z pierwszą literą zamienioną na wielką. Jeśli wyrażenie zostanie pominięte, funkcja używa wartości zmiennej $_: ucfirst WYRAŻENIE ucfirst unpack — rozpakowywanie danych Funkcja unpack dekoduje tekst skompresowany funkcją pack: unpack WZORZEC, WYRAŻENIE wzorzec konstruuje się tak samo, jak dla funkcji pack, na przykład: $string = pack("ccc", 88, 89, 90); print join(", ", unpack "ccc", $string); 88, 89, 90 Oto inny przykład, w którym dekodujemy wartości szesnastkowe skompresowane funkcją vec (więcej o tej funkcji dowiesz się dalej w tym rozdziale) na ciąg zer i jedynek: vec ($data, O, 32) = 0x11; $bitstring = unpack("B*", $data); print $bitstring; 00000000000000000000000000010001 unshift — przesuwanie wartości w tablicy z ich dodawaniem Funkcja unshift dodaje na początek tablicy listę wartości: unshift TABLICA, LISTA Oto przykład: @array = (4, 5, 6) ; unshift @array, l, 2, 3; print join (", ", @array), l, 2, 3, 4, 5, 6 values — wartości asocjacji W kontekście list funkcja values zwraca listę zawierającą wartości z asocjacji, a w kontekście skalarnym — liczbę wartości z asocjacji: values ASOCJACJA Funkcji values można użyć do realizacji iteracji po asocjacji, jak poniżej: $hash( kanapka) = 'szynka z serem’; $hash{picie) = 'dietetyczna cola'; foreach $value (values %hash) { print "$value\n"; } dietetyczna cola szynka z serem vec — wektor składający się z liczb całkowitych bez znaku Funkcja vec traktuje przekazane jej wyrażenie jako tablicę jednowymiarową — czyli wektor — liczb całkowitych bez znaku i zwraca wartość pola bitowego zaczynającego się od wskazanego OFFSETU: vec WYRAŻENIE, OFFSET, POLEBITOWE polebitowe wskazuje, ile bitów ma być zarezerwowanych dla każdej pozycji wektora (liczba ta musi być potęgą dwójki i mieć wartość od l do 32). Można również funkcji vec przypisać wartość. Oto przykład pokazujący sposób binarnego wyświetlania cyfr szesnastkowych: $hexdigit = 0xA; vec ($data, 0, 8) = $hexdigit; print vec($data, 3, 1); print vec($data, 2, 1); print vec($data, l, 1); print vec($data, 0, 1); 1010 Rozdział 11. Funkcje wbudowane: I/O i komunikacja między procesami W skrócie Wejście i wyjście Perla (I/O) to nie tylko wykorzystywanie urządzeń zewnętrznych — czyli odczytywanie informacji z klawiatury i wyświetlanie danych wyjściowych na ekranie — ale też łączność z innymi procesami i używanie plików. Wszystkie te operacje wejścia-wyjścia są realizowane przy użyciu uchwytów plików. Z uwagi na to, że wiedza na ten temat jest dość obszerna, całość zostanie omówiona w dwóch rozdziałach: w tym zajmiemy się wejściem i wyjściem na konsolę oraz komunikacją międzyprocesową (IPC, InterProcess Communicatiori), w następnym — używaniem plików dyskowych. Formaty Perla Kiedy trzeba coś wyświetlić, Perl oferuje narzędzie pozwalające tworzyć proste raporty i wykresy: formaty Perla. Tak naprawdę formatowanie tekstowych raportów było podstawową częścią funkcji Perla (przypomnijmy, że Perl to skrót od Practical Extraction and Reporting Language, czyli Praktyczny język raportowania i pobierania danych). Formaty Perla umożliwiają formatowanie wyników ukazujących się na konsoli, a więc wyrównane do prawej strony, centrowane lub wyrównane do lewej (zresztą formatów można też używać do zapisywania danych w pliku). Istnieje też możliwość określania szerokości drukowanych pól oraz ich rozmieszczenia w poszczególnych wierszach. Formaty Perla są dość proste (nie są na przykład obsługiwane arkusze stylów), ale i tak często stosuje się je — przy programowaniu CGI — do przygotowywania sformatowanego tekstu, zatem teraz je omówimy. Formaty, jak pakiety i procedury, wymagają deklaracji. Aby format zadeklarować, podaje się uchwyt pliku, którego format ma dotyczyć a następnie podaje się wiersze opisowe składające się ze znaków, takich jak @, ^, <, | i innych, planując w ten sposób wygląd wiersza. W kolejnym etapie podaje się wiersze zawierające dane, które mają być pokazane. Deklarację formatu kończy się kropką, a sformatowane dane wyświetla się za pomocą funkcji write. Oto przykład, w którym wyrównujemy do lewej jeden fragment danych, podczas gdy drugi wyrównujemy do prawej. Długość całego wiersza opisu (czyli @<<<<<<<<<<@>>>>>>>>>>) decyduje o długości odpowiedniego wiersza wynikowego: format STDOUT = @<<<<<<<<<<@>>>>>>>>>> $textl $text2 $textl = “Hello”; $text2 = “there!”; write; # Format STDOUT używany jest domyślnie Hello there! Kolejnym dużym zagadnieniem jest komunikacja międzyprocesowa (choć niewiele jak na razie poświęcono jej książek), której opis w tym rozdziale będzie dobrym początkiem. Zaczniemy od użycia poleceń exec i syscall do wywoływania funkcji systemowych. Później uruchomimy nowy proces, który przechwyci dane, odczytamy dane wysłane z innego procesu oraz stworzymy procesy potomne, z których dane będzie można czytać i odbierać. Przyjrzymy się też użyciu gniazd (socket) do komunikacji za pośrednictwem Internetu. Zapoznamy się także z łączeniem i zagnieżdżaniem obiektów w Windows (OLE). Z uwagi na to, że temat jest bardzo obszerny, na pewno go tutaj nie wyczerpiemy — więcej informacji na temat IPC (czyli komunikacji międzyprocesowej) należy szukać w dokumentacji Perla. Zagadnienia: print — drukowanie danych listowych Funkcja print drukuje listy do uchwytu pliku. W przypadku niewskazania uchwytu, zostanie użyty stdout lub domyślny kanał wyjściowy (do ustawienia innego kanału domyślnego niż STDOUT stosuje się funkcję select, opisaną w rozdziale 12, w punkcie „select — określanie domyślnego uchwytu pliku wyjścia"). Jeśli nie zostanie podana lista wyświetlanych wartości, użyta zostanie zmienna $_: print UCHHYTPLIKU LISTA print LISTA print Funkcji print używaliśmy w tej książce już nie raz, zatem powinna być już dobrze znana. Funkcja print zwraca wartość prawdy, jeśli drukowanie się powiedzie, a choć dotąd stosowaliśmy tylko stdout, to można używać też innych uchwytów plików, czym zajmiemy się w następnym rozdziale. Oto przykład: $a = "Hello"; $b = " to"; $c = " you"; $«i = " from"; $e = " Perl!"; print $a, $b, $c, $d, $e; Hello to you from Perl! Zagadnienie związane: Stosowanie interpolacji w tekście Skomplikowane interpolacje select — określanie domyślnego uchwytu pliku wyjścia printf — drukowanie formatowanych danych z list Funkcja printf drukuje sformatowane dane do uchwytu pliku. Jeśli uchwyt zostanie pominięty, printf używa STDOUT: printf UCHWYTPLIKU FORMAT, LISTA printf FORMAT, LISTA Funkcja printf jest bardzo podobna do sprintf, tyle tylko, że sformatowane dane są drukowane do uchwytu pliku. Funkcja sprintf jest równoważna print uchwytpliku sprintf (format, lista) — poza tym, że nie jest uwzględniany separator rekordu wyjściowego $\. Ciąg formatujący jest taki sam, jak w przypadku funkcji sprintf, a więcej na ten temat możesz dowiedzieć się w punkcie „sprintf— formatowanie tekstu", w poprzednim rozdziale. Dane, które mają być zaprezentowane, umieszcza się na LIŚCIE. Jeśli kod jest zależny od ustawień lokalnych, czyli w kodzie użyto use locale, kropka dziesiętna jest formatowana zgodnie z wartością LO_NUME-RIC. Oto przykłady użycia printf: $value = 1234.56789; printf "%.4f\n", $value; 1234.5679 1234.5679 printf "%.5f\n", $value; 1234.56789 printf "%6.6f\n", $value; 1234.567890 printf "%+.4e\n", $value; +1.234e+003 Zagadnienie związane: sprintf — formatowanie tekstu Użycie <> do odczytu danych Do odczytywania danych wejściowych ze STDIN można użyć wyrażenia o, jak poniżej: while (<>) { print; } Domyślnie wyrażenie o powoduje przypisanie danych ze strumienia wejściowego zmiennej domyślnej, $_ — wyrażenie o to skrót od . Można też, stosując zapis , pobierać dane z innego pliku. getc — pobranie znaku Funkcja getc zwraca następny znak wejściowy z uchwytu pliku. Jeśli plik zostanie pominięty, użyty zostanie stdin: getc UCHWYTPLIKU getc Ku niezadowoleniu wielu programistów funkcji getc nie można użyć do pobierania danych niebuforowanych, jeśli nie zostanie to umożliwione bezpośrednio w systemie. Normalnie getc przed odczytaniem danych oczekuje na wciśnięcie przez użytkownika klawisza ENTER. Jednak w niektórych systemach można buforowanie wiersza wyłączyć, jak w poniższym kodzie, w którym za pomocą funkcji system odczytuje się do dziesięciu znaków (zajrzyj do punktu „IPC: system — rozgałęzienie procesów i uruchomienie polecenia systemowego" w dalszej części tego rozdziału): system "stty cbreak &l"; for ($loop_index = 0; $loop_index <= 9; $loop_index++) { $char = getc(STDIN); print $char; } write — zapis sformatowanego rekordu Funkcja write zapisuje sformatowany rekord do pliku — przy użyciu formatu z danym uchwytem pliku związanego. Jeśli plik zostanie pominięty, write zastosuje stdout: write UCHWYTPLIKU write WYRAŻENIE write W przypadku, gdy zamiast pliku podane zostanie WYRAŻENIE, Perl je wyliczy i wynik potraktuje jako uchwyt pliku. Oto przykład użycia write, w którym łączymy format ze STDOUT, a następnie wyświetlamy formatowany tekst za pomocą funkcji write: format STDOUT = @<<<<<<<<<<<@>>>>>>>>>>>>>>> $textl $text2 . $textl = "Hello"; $text2 = "there!"; write; Hello there! Pamiętaj o zakończeniu deklaracji formatu kropką. Formaty: wyrównanie tekstu do lewej Aby wyrównać do lewej tekst w polu formatu, używa się znaków < za znakiem @ (znak @ to początek pola). Szerokość pola jest określona przez liczbę znaków < za @, jak w poniższym przykładzie: format STDOUT = @<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<<@<<<<<<<<<<@<<<<<<< $firstname $lastname $ID $extension $firstname = "Cary"; $lastname = "Grant"; $ID = 1234; $extension = x456; write; Cary Grant 1234 x456 Formaty: wyrównanie tekstu do prawej Aby wyrównać w polu formatu tekst do prawej strony, za znakiem @ — rozpoczynającym pole — używa się znaków >. Tak jak w przypadku użycia <, szerokość pola określa liczba znaków >: format STDOUT = @>>>>>>>>>>>>>>>>>>>> $text . $text = "Hello!"; write; Hello! Formaty: centrowanie tekstu Aby wycentrować tekst w polu formatu, zamiast znaków < czy > używa się |: format STDOUT = @||||||||||||||||||||||||||||||||||||||||||||||| $text . $text = "Hello!"; write; Hello! Formaty: prezentacja liczb Aby opisać pole liczbowe, używa się znaków # z opcjonalną kropką dziesiętną, jak w poniższym przykładzie, gdzie podajemy liczbę miejsc dziesiętnych w wyświetlanej wartości: $pi = 3.1415926; format STDOUT = @.## @.####### $pi $pi write; 3.14 3.1415926 Znaki użyte jako kropki dziesiętne są określone przez ustawienie lokalne LC_NUMERIC. Formaty: formatowanie danych wielowierszowych W Perlu wielowierszowe formaty nie stanowią problemu: po prostu w poszczególnych wierszach umieszcza się tyle obrazków i zmiennych, ile jest potrzebnych (trzeba tylko pamiętać o kropce na końcu formatu), jak w poniższym przykładzie: format STDOUT = @<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<< $textl $text2 @<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<<<<< $text3 $text4 $textl = "Hello"; $text2 = "there!"; $text3 = "How're"; $text4 = "things?"; write; Hello there! How’re things? Formatowanie: rozdzielanie tekstu do formatowania danych wielowierszowych Stosując znak ~ można rozbijać długi tekst na szereg pól. Jeśli użyty zostanie znak ^, tekst potrzebny do wypełnienia danego pola jest wycinany z początku całego tekstu i wyświetlany (sam wejściowy tekst też jest modyfikowany). Oto przykład, w którym rozdzielamy powitanie w różnych językach z jednego słowa $text: $: = "; format STDOUT = Angielski: ^<<<<< $text $text Niemiecki: ^<<<<<<<<<< $text $text Francuski: ^<<<<<<< $text $text = "Hello!Guten Tag!Bonjour!"; write; Angielski: Hello! Niemiecki: Guten Tag! Francuski: Bonjour! Formatowanie: wielowierszowe dane bez formatowania Użycie w formacie wyrażenia @* powoduje wyświetlenie podanego tekstu bez zmian, w tym są umieszczane także znaki nowego wiersza. Oto przykład: format STDOUT = @* $text . $text = "Here\nis\nthe\ntext."; write; Here is the text. Formaty: dane w nagłówku raportu Stosując nazwę uchwytu pliku z dołączonym wyrażeniem _TOP, można sformatować nagłówek dokumentu dotyczący danego pliku. Nagłówek jest wyświetlany na górze każdej strony raportu. Oto przykład, w którym tworzymy nagłówek dla naszych danych: format STDOUT_TOP = Pracownicy Imię Nazwisko ID Rozszerzenie ------------------------------------------------------------------ . format STDOUT = @<<<<<<@<<<<<<<<<<<@<<<<<<<<@<<<<<<< $firstname $lastname $ID $extension . $firstname = "Cary"; $lastname = "Grant"; $ID = 1234; $extension = x456; write; Pracownicy Imię Nazwisko ID Rozszerzenie Cary Grant 1234 x456 Formaty: zmienne formatów Z formatami związanych jest wiele zmiennych specjalnych Perla: $~ — nazwa bieżącego formatu., $A — nazwa bieżącego formatu nagłówka, $% — bieżący numer strony, $= — liczba wierszy na stronie, $l — zwraca wartość prawdy, jeśli dane wyjściowe są automatycznie usuwane, $^L — tekst dołączany do wyniku na początku każdej strony (poza pierwszą). Oto przykład, w którym tworzymy format i wiążemy go z aktualnym kanałem wyjściowym, stosując zmienną $~: format standardformat = @|||||||||||||||||||||||||||||||||||||||||||||||||| $text . $text = "Hello!"; $~ = standardformat; write; Hello! warn — wyświetlenie ostrzeżenia Wiemy już, jak używać STDIN i STDOUT, a co ze STDERR (domyślnie związanym z urządzeniami wejścia-wyjścia)? Do strumienia STDERR można wysyłać dane za pomocą funkcji warn: warn LISTA Inną metodą jest oczywiście drukowanie przy użyciu funkcji print, bezpośrednio do stderr. Funkcja warn wyświetla komunikat na STDERR, ale w przeciwieństwie do die nie kończy przy tym działania aplikacji ani nie generuje błędu. Jeśli funkcja ta zostanie wywołana bez parametrów, da ostrzeżenie mniej więcej takiego typu: Warning: something's wrong at script.pl line 1. Uwaga: coś jest nie tak w script.pl wiersz 1. Jeśli ustawiona zostanie zmienna $@, wyświetli się jej zawartość, a dalej tabulator i miejsce wystąpienia: $@ = "Przepełnienie"; warn; Przepełnienie ...caught at script.pl line 2. Funkcji warn można użyć jako operatora list: warn "Źle", " się", " dzieje", " w państwie", " duńskim"; Źle się dzieje w państwie duńskim at script.pl linę 1. Jeśli ustawiona zostanie obsługa sygnału ostrzeżeń, nic nie zostanie wyświetlone (więcej informacji o sygnałach znajdziesz dalej w tym rozdziale, w punkcie „IPC: wysyłanie sygnału do innego procesu"): local $SIG{__WARN__} = sub {}; IPC: exec — wywołanie polecenia systemowego Funkcja exec wywołuje polecenie systemu operacyjnego: exec LISTA exec PROGRAM LISTA Funkcja ta uruchamia polecenie, ale nie powraca do miejsca wywołania (aby powrócić, używa się funkcji system). Funkcja exec nie zadziała i powraca do punktu wywołania tylko wtedy, gdy podane polecenie systemowe nie istnieje. Warto zauważyć, że w różnych systemach dostępne mogą być różne polecenia. Oto przykład, który zadziała zarówno w Windows, jak i w systemie Unix: exec 'echo Hello!’; Hello! Zwykle można bez problemów używać exec w połączeniu z nazwą polecenia systemowego, podając mu parametry, ale sposób traktowania przez exec parametrów jest skomplikowany. W systemie Unix działa to tak: jeśli lista zawiera więcej niż jeden parametr (lub jeśli lista jest tablicą zawierającą więcej niż jedną wartość), exec wywołuje na tej liście execvp(3). Jeśli podany zostanie pojedynczy parametr skalarny (lub tablica zawierająca jeden element), Perl wyszukuje znaków specjalnych interpretera poleceń. W sytuacji, gdy znaki takie zostaną znalezione, parametr jest przekazywany interpreterowi poleceń. Jeśli żadne takie znaki nie zostaną odnalezione, parametr jest rozbijany i przekazywany execvp. IPC: system — rozgałęzienie procesów i uruchomienie polecenia systemowego Funkcja system działa dokładnie tak samo jak exec, ale najpierw tworzy proces potomny, przekazując mu wywołanie i czekając na jego zakończenie: system LISTA system PROGRAM LISTA Przekazywania parametrów funkcji system dotyczą te same zastrzeżenia, co funkcji exec (więcej informacji na ten temat znajdziesz w poprzednim punkcie). IPC: odczytywanie danych z innego programu Załóżmy, że mamy program printem wyświetlający „Hello!": print 'Hello!'; Czy można wczytać dane wyjściowe tego programu do innego programu? Tak, wystarczy tylko skorzystać z przetwarzania potokowego. Przy otwieraniu pliku można przekazać do niego dane wyjściowe programu, umieszczając za nazwą programu znak l: open(FILEHANDLE, "printem |"); Użyliśmy tutaj instrukcji open, aby przekazać wyniki programu printem do programu bieżącego (więcej o open w następnym rozdziale). Mechanizmy potokowe są przy programowaniu komunikacji międzyprocesowej ogromnie ważne, umożliwiają bezpośrednie wprowadzanie lub wyprowadzanie danych do i z programów. Potoki najlepiej działają w systemie Unix, choć wersje Perla na Win32 także zawiera częściową ich obsługę. Po utworzeniu potoku można po prostu czytać dane ze związanego z nim uchwytu pliku: open(UCHWYTPLIKU, "printem |"); while () { print; } close(FILEHANDLE); Hello! Instrukcji open można użyć do utworzenia potoku do odczytywania danych z pliku lub wysyłania danych do pliku (zobacz następny punkt), ale nie obu jednocześnie. Aby jeden program mógł przekazywać dane w obu kierunkach jednocześnie, trzeba użyć IPC::Open2 z modułu Perl IPC. Zagadnienie związane: open — otwarcie pliku IPC: wysyłanie danych do innego programu Załóżmy, że mamy program readem odczytujący dane i wyświetlający wszystko, co otrzymał: while(<>) { print; } Jak wysłać dane do readem? W tym celu stosuje się znak | przed nazwą programu, przy otwieraniu pliku: open(FILEHANDLE, "| readem"); print FILEHANDLE "Hello!"; close(FILEHANDLE); Hello! Instrukcji open można użyć do utworzenia potoku do odczytywania danych z pliku lub wysyłania danych do pliku (zobacz następny punkt), ale nie obu jednocześnie. Aby jeden program mógł przekazywać dane w obu kierunkach jednocześnie, trzeba użyć IPC::Open2 z modułu Perl IPC. Zagadnienie związane: open — otwarcie pliku IPC: zapisywanie danych do procesu potomnego Instrukcję open stosujemy też do utworzenia procesu potomnego bieżącego procesu. Następnie z procesu potomnego można odczytywać dane, jeśli tylko open jako parametr otrzyma " | - ": if (open(CHILDHANDLE, "|-")) Instrukcja taka utworzy nowy uchwyt pliku dla procesu potomnego, CHILDHANDLE, a następnie rozdzieli bieżący proces (czyli utworzy proces potomny). Proces potomny i bieżący będą używać tego samego kodu, ale open (childhandle, " | -") w procesie potomnym zwróci 0 (czyli fałsz), wobec czego korzystamy z powyższej instrukcji if do sprawdzenia, czy procesem bieżącym jest proces rodzic, czy proces potomny. Jeśli jesteśmy w procesie rodzica, możemy procesowi potomnemu wysłać pewne dane, a następnie proces ten zamknąć: if (open(CHILDHANDLE, "!-")) { print CHILDHANDLE "Oto nieco tekstu."; close(CHILDHANDLE); Jeśli jesteśmy z kolei w procesie potomnym, możemy pokazać dane przesłane z procesu rodzica: if (open(CHILDHANDLE, "|-")) { print CHILDHANDLE "Oto nieco tekstu."; close(CHILDHANDLE); } else { print <>; exit; } Niczego więcej już nam nie trzeba, proces potomny z tego przykładu wyświetli dane przekazane z procesu rodzica: Oto nieco tekstu. Zagadnienie związane: open — otwarcie pliku lPC: zapisywanie danych do procesu rodzica W poprzednim punkcie odczytywaliśmy w procesie potomnym dane. Można też postąpić odwrotnie: użyć instrukcji open do utworzenia procesu potomnego i pisać z tego procesu potomnego do procesu rodzica — należy wtedy jako parametr open podać "-|" : if (open (CHILDHANDLE, "-|")) Przekazanie open jako parametru "-|" powoduje rozdzielenie procesu bieżącego i utworzenie procesu potomnego. Oba procesy używają tego samego kodu, ale i f (o-pen (CHILDHANDLE, "- 1" ) ) zwróci wartość O (czyli false) w procesie potomnym, zatem instrukcja i f pozwala określić, czy jesteśmy w procesie rodzica, czy potomnym. Jeśli jesteśmy w procesie rodzica, odbieramy z procesu potomnego wiersz danych i wyświetlamy go: if (open (CHILDHANDLE, "-|")) { print ; close (CHILDHANDLE) ; Jeśli z kolei jesteśmy w procesie potomnym, instrukcja print pozwala wysłać dane do procesu rodzica: if (open (CHILDHANDLE, "-|")) { print ; close (CHILDHANDLE); } else { print "Oto nieco tekstu."; exit; } I to nam już wystarcza, proces rodzic wyświetli dane przekazane z procesu potomnego: Oto nieco tekstu. Zagadnienie związane: open — otwarcie pliku IPC: wysyłanie sygnału do innego procesu Procesy w systemie Unix mogą się ze sobą komunikować za pomocą sygnałów. W celu sprawdzenia, czy w danej wersji systemu sygnały są obsługiwane, można użyć polecenia kill: %kill -l HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH LOST USR1 USR2 Perl umożliwia przechwytywanie tych sygnałów przez powiązanie z nimi funkcji je obsługujących w specjalnej asocjacji %SIG. Kluczami tej asocjacji są używane sygnały. Oto przykład. Rozdzielamy proces bieżący, tworząc proces potomny, który wyśle sygnał INT do procesu rodzica. Zaczynamy od utworzenia procesu potomnego: if (open(CHILDHANDLE, "|-")) Następnie dodajemy obsługę sygnału int: anonimową procedurę wyświetlającą— po otrzymaniu sygnału — komunikat: if (open(CHILDHANDLE, "|-")) { $SIG{INT} = sub {print "Otrzymano komunikat.\n"}; Proces potomny będzie potrzebował ID procesu rodzica — identyfikator ten jest zapisywany w specjalnej zmiennej Perla, $$ — musimy zatem tę wartość przekazać procesowi potomnemu: if (open(CHILDHANDLE, "|-")) { $SIG{INT} = sub {print "Otrzymano komunikat.\n"}; print CHILDHANDLE "$$"; close(CHILDHANDLE); W procesie potomnym zapiszemy identyfikator procesu rodzica w zmiennej $parentid, a następnie przy pomocy funkcji kill wyślemy sygnał INT: if (open(CHILDHANDLE, "|-")) { $SIG(INT) = sub {print "Otrzymano komunikat.\n"}; print CHILDHANDLE "$$"; close(CHILDHANDLE); } else { chomp($parentpid = <>); kill INT => $parentpid; exit; } To już wszystko, oto wynik wyświetlony przez proces rodzica po otrzymaniu sygnału int z procesu potomnego: Otrzymano komunikat. Zagadnienie związane: open — otwarcie pliku IPC: użycie gniazd Gniazda umożliwiają tworzenie połączeń przez Internet (a także lokalnych). Jest to temat zbyt obszerny, aby go teraz tu omówić, ale możemy przynajmniej pokazać, jak silne jest to narzędzie. Przygotujmy taki przykład, w którym użyjemy protokołu UDP. Program klient będzie pisał dane do programu serwera za pośrednictwem Internetu. Wprawdzie program klient możemy uruchamiać na dowolnym komputerze podłączonym do Internetu, to jednak program serwerowy powinien działać na komputerze dostawcy Internetu (ISP). Program klienta zaczniemy, importując z modułu IO Perla IO::Socket: use IO::Socket; Następnie, stosując IO::Socket::INET->new, utworzymy nowe gniazdo, metodzie tej przekażemy używany protokół (UDP), port serwera, do którego chcemy sięgnąć (użyjemy portu 4321. W systemach Unix porty poniżej 1024 są zarezerwowane na potrzeby systemu) oraz nazwę ISP: use IO::Socket; $socket = IO::Socket::INET->new(Proto => 'udp’; PeerPort => 4321, PeerAddr => 'servername.com'); Teraz trzeba tylko wysłać na serwer dane — użyjemy do tego metody send: use IO::Socket; $socket = IO::Socket::INET->new(Proto => 'udp', PeerPort => 4321, PeerAddr => 'servername.com'); $socket->send('Hello!'); Następnie trzeba napisać program serwerowy. Powinien on działać w chwili wysłania komunikatu z klienta, zatem zaczniemy od dodania IO::Socket i wyświetlenia komunikatu „Czekam...\n": use IO::Socket; print "Czekam...\n"; Następnie stosując metodę IO::Socket::INET->new, utworzymy na serwerze nowe gniazdo, nakażemy użyć protokołu (UDP) oraz wskażemy używany port: use IO::Socket; print "Czekam...\n"; $socket = IO::Socket::INET->new(LocalPort => 4321, Proto => 'udp'); Aby móc odbierać dane z klienta, użyjemy metody recv obiektu $socket, informując przy tym, że ma być odebranych nie więcej niż 128 bajtów. Metoda ta będzie czekała na dane, a kiedy się one pojawią, wyświetli je: use IO::Socket; print "Czekam...\n"; $socket = IO::Socket::INET->new(LocalPort=>4321, Proto => 'udp’); $socket->recv($text, 128); print "Odebrano następujący komunikat: $text\n"; Po uruchomieniu programu klienta program na serwerze wyświetli następujące dane: Czekam... Odebrano następujący komunikat: Hello! IPC: OLE w Win32 Metodą obsługi komunikacji międzyprocesowej w systemie Windows jest użycie serwerów OLE. ActiveState Perl for Win32 obsługuje OLE; aby tego mechanizmu użyć, tworzy się w programie obiekt serwera OLE, jak na przykład Microsoft Excel. Po utworzeniu obiektu można stosować jego metody. Podczas komunikacji międzyprocesowej w Windows istotne stają się typy danych — załóżmy na przykład, że mamy program napisany w C++ i chcemy skomunikować się z programem napisanym w Pascalu. Aby uniknąć tego typu problemów, pakiet Perl Win32 zawiera zestaw standardowych typów variant (jest to wygodna metoda obsługi danych, których typ tak naprawdę trudno określić). Typy te przedstawiono w tabeli 11.1, która informuje, na jakie typy danych OLE są konwertowane typy Perla przed przekazaniem danych serwerowi OLE. Tabela 11.1. Typy danych OLE Typ danych OLE Standardowych typ danych OLE::VT_BOOL Wartości logiczne OLE. OLE::VT_BSTR Tekst OLE (char* znany z języka C). OLE::VT_CY Waluta OLE. OLE::VT_DATE Data OLE. OLE::VT_I2 Liczba całkowita ze znakiem (2 bajty). OLE::VT_I4 Liczba całkowita ze znakiem (4 bajty). OLE::VT_R4 Liczba zmiennoprzecinkowa (4 bajty). OLE::VT_R8 Liczba zmiennoprzecinkowa (8 bajtów). OLE::VT_UI1 Znak bez znaku. Na przykład liczby całkowite Perla zostaną przekształcone na VT_I4, liczby zmienno-przecinkowe podwójnej precyzji na VT_R8 i tak dalej. Pakiet Perl Win32 wykonuje te konwersje automatycznie. Aby utworzyć w programie obiekt OLE, trzeba włączyć moduł OLE: use OLE; Następnie funkcja CreateObject pozwala obiekt utworzyć. Przyjrzyjmy się przykładowi. Użyjemy Microsoft Excela, aby dodać do siebie dwie liczy 2, a następnie wyświetlimy wynik. Zaczniemy od zapisania parametrów w zmiennych $operandl i $operand2: use OLE; $operandl = '2'; $operand2 = '2'; W kolejnym etapie za pomocą funkcji CreateObject utworzymy obiekt OLE $excelobject. Funkcji tej przekażemy wartość OLE i wskażemy typ obiektu OLE, jaki ma zostać utworzony — server.class, gdzie server to zarejestrowana nazwa serwera OLE, a class to klasa tworzonego obiektu (zwykle serwery OLE obsługują wiele różnych klas —jeśli ma się szczęście, w dokumentacji serwera można znaleźć informację o tym, jakich metod można użyć w poszczególnych klasach). W naszym przypadku utworzymy arkusz Excela: use OLE; $operandl = '2'; $operand2 = '2'; $excelobject = CreateObject OLE 'Excel.Sheet'; Teraz możemy już swobodnie używać metod nowego obiektu. Najpierw załadujemy $operandl do komórki (1,1) arkusza, zaś $operand2 do komórki (2,1) i wynik wyliczenia sumy umieścimy w komórce (3,1): use OLE; $operandl = '2' ; $operand2 = '2' ; $excelobject = CreateObject OLE 'Excel.Sheet'; $excelobject->Cells(1,1)->{Value} = $operandl; $excelobject->Cells(2,1)->{Value} = $operand2; $excelobject->Cells(1,1)->{Formula} = '=R1C1 + R2C1'; W tym momencie pozostało już tylko wyświetlić wynik: use OLE; $operandl = '2'; $operand2 = '2'; $excelobject = CreateObject OLE 'Excel.Sheet'; $excelobject->Cells(l,1)->(Value) = $operandl; $excelobject->Cells(2,1)->{Value} = $operand2; $excelobject->Cells(l,1)->{Formula) = '=R1C1 + R2C1'; $sum = $excelobjact->Cells(3,l)->{Value); $excelobject->Quit(); print "Microsoft Excel twierdzi, że ", "$operandl + $operand2 = $sum.\n"; Microsoft Excel twierdzi, że 2 + 2 = 4. Rozdział 12. Funkcje wbudowane: obsługa plików W skrócie W tym rozdziale zajmiemy się obsługą plików w Perlu, szczególnie funkcjami używanymi do obsługi plików fizycznych (czyli znajdujących się na dysku), nazw plików i katalogów. Jest to kolejny obszerny temat w Perlu i choć nie będzie możliwe omówienie go tutaj w całości, to przynajmniej poznamy go na tyle, aby móc się w nim poruszać (jedną z przyczyn obszemości tego tematu jest obecność wielu powtórzeń. Motto Perla — „da się to zrobić na więcej niż jeden sposób" — w przypadku obsługi plików jest wyjątkowo prawdziwe). Uwaga dla osób z „uniksofobią": obsługa plików w Perlu była oparta pierwotnie na systemie plików Unix, co nadal w dużym stopniu jest widoczne — używa się uprawnień plików systemu Unix, odnośników symbolicznych i tak dalej. Przydatne może być zrobienie kilku eksperymentów z posiadaną wersją Perla, szczególnie jeśli chodzi o ustawianie uprawnień do plików. Wszystko o obsłudze plików Większości programistów nie jest obca obsługa plików: aby użyć danych z pliku, otwiera się go, pobiera jego uchwyt — wtedy jest tworzony kanał wejściowy lub wyjściowy, więc w dalszych operacjach obsługi pliku, odczycie i zapisie, tego uchwytu się używa. Po skończeniu używania pliku zamyka się go. W tym rozdziale dokładnie omówimy cały ten proces. Stosować będziemy nie tylko uchwyty plików, ale też będziemy zarządzać plikami i katalogami. Przyjrzymy się też, jak tworzyć i używać bazę DBM w systemie Unix, gdyż bazy takie są powszechnie stosowane przy programowaniu CGI. Trzeba tu przypomnieć o pewnych konwencjach. Po pierwsze, nazwy uchwytów plików w Perlu zapisuje się zwykle wielkimi literami, aby odróżnić je od słów zarezerwowanych dla języka, gdyż uchwyty nie muszą być poprzedzane wyróżnikami, jak $ (jeśli chcemy traktować uchwyt pliku jak zmienną — na przykład skopiować go — musimy użyć odpowiedniego typu ogólnego). Kolejną sprawą, o której trzeba pamiętać, jest to, że obsługa plików stanowi prawdopodobnie najbardziej narażoną na błędy dziedzinę programowania, zatem przy końcu operatorów, które mogą zawieść, dobrze jest dodać frazę or die. Ostatnia uwaga jest taka, że w systemie Unix do rozdzielania katalogów w ścieżce dostępu używa się ukośnika /, zaś w systemach używających lewego ukośni-ka \, jak DOS czy Windows, separatory te trzeba specjalnie zapisywać: open (FILEHANDLE, "tmp\\file.txt") or die ("Nie mogą otworzyć pliku file.txt"); while () { print; } Z uwagi na to, że w Perlu pliki są tak często używane, informacje związane z tym rozdziałem są też rozrzucone w innych miejscach tej książki, na przykład w rozdziale 4. opisano operatory plików -X, w rozdziale 9. omówiono zmienne specjalne obsługujące pliki (jak $/ z separatorem rekordu wejściowego, $, z separatorem rekordu wyjściowego, $| opisująca buforowanie i tak dalej), a w rozdziale 10. zaprezentowano funkcje, które pozwalają pakować dane w rekordy o dostępie swobodnym z rekordami o stałej długości (są to pack, unpack. i vec). W Perlu, jak to już niejednokrotnie zaznaczono, wszystko można zrobić na więcej niż jeden sposób. Jeśli czegoś nie można znaleźć na przykład w zestawie narzędzi obsługi plików, to może to znajdować się gdzieś indziej. Perl nie zawiera wbudowanej funkcji pozwalającej na kopiowanie plików, ale w module 10: :File istnieje funkcja copy, która pozwala to zrealizować. Poza tym zawsze można zajrzeć do kilkudziesięciu funkcji modułu POSIX. Zagadnienia: open — otwarcie pliku Do otworzenia pliku używa się funkcji open: open FILEHANDLE, WYRAŻENIE open FILEHANDLE Funkcja ta otwiera plik o nazwie określonej wyrażeniem i wiąże uchwyt tego pliku z filehandle. Po udanym otwarciu można używać uchwytu przy wykonywaniu operacji na pliku. Jeśli wyrażenie zostanie pominięte, zakłada się, że zmienna o nazwie takiej samej jak filehandle zawiera nazwę pliku. * Jeśli otwarcie się powiedzie, funkcja open zwraca wartość prawdy (niezerowaj w przypadku otwierania potoku zwracany jest identyfikator podprocesu), natomiast — gdy otwarcie się nie powiedzie, zwracana jest wartość niezdefiniowana. Oto sposób podawania nazwy pliku w wyrażeniu: * Jeśli nazwa pliku została poprzedzona znakiem < lub nie ma żadnego przedrostka, funkcja otwiera wskazany plik jako wejściowy. * Jeśli nazwa pliku poprzedzona została znakiem >, plik jest czyszczony i otwierany jako wyjściowy (w razie potrzeby jest on tworzony). * Jeśli nazwa pliku poprzedzona została znakiem >>, plik jest otwierany do dopisywania (w razie potrzeby jest tworzony). * Jeśli przed > lub < znajdzie się znak +, funkcja umożliwia zarówno odczyt, jak i zapis do pliku (należy raczej korzystać z zapisu +<, jeśli plik ma być aktualizowany, gdyż +> najpierw zeruje plik). * Jeśli nazwa pliku jest poprzedzona znakiem |, podana nazwa jest traktowana jako polecenie potoku, z którego dane mają być wczytywane (zajrzyj do rozdziału 11.). * Jeśli za nazwą pliku znajdzie się znak |, podana nazwa jest traktowana jako polecenie potoku, do którego skierowane mają być dane wynikowe programu (zajrzyj do rozdziału 11.). * Jeśli jako nazwa pliku zostanie użyty znak -, otwierany jest STDIN. * Jeśli jako nazwa pliku zostanie użyty znak >-, otwierany jest STDOUT. * Jeśli wyrażenie zaczyna się od >&, reszta wyrażenia jest traktowana zależnie od jego typu: w sytuacji, gdy jest to tekst, interpretowana jest jako uchwyt pliku; jeśli natomiast jest liczbą, jest interpretowana jako deskryptor pliku w systemie Unix. Znaku & można użyć także po >>, <, +>, +>> lub +<. * Jeśli wyrażenie ma postać <&=n, gdzie n jest liczbą, n jest traktowane jako deskryptor pliku i obsługiwany tak, jak byłby obsługiwany przez funkcję fdopen z języka C. * Jeśli za pomocą |- lub -| otwierany jest potok, funkcja najpierw rozdziela proces i zwraca identyfikator procesu potomnego (więcej informacji na ten temat znajdziesz w rozdziale 11.). Oto przykład, w którym otwieramy plik jako wyjściowy i umieszczamy w nim tekst: open (FILEHANDLE, ">hello.txt") or die ("Nie mogę otworzyć pliku hallo.txt"); print FILEHANDLE "Hello!"; close (FILEHANDLE); Hello! Zagadnienie związane: IPC: zapisywanie danych do procesu potomnego IPC: zapisywanie danych do procesu rodzica IPC: wysyłanie sygnału do innego procesu close — zamknięcie pliku Funkcji close używa się do zamknięcia otwartego pliku lub potoku. Kiedy jego używanie zostanie zakończone, wszystkie buforowane dane są przesyłane do pliku lub potoku: close FILEHANDLE close Funkcja ta zwraca wartość prawdy, jeśli uda się czyszczenie buforów pliku i plik zostanie prawidłowo zamknięty. Jeśli nie zostanie podany uchwyt pliku, funkcja zamyka aktualnie wybrany plik (zajrzyj do punktu „select — określanie domyślnego uchwytu pliku wyjścia", dalej w tym rozdziale). Przy zamykaniu potoku funkcja close czeka na zakończenie działania potoku procesu, aby umożliwić obsługę wyników tego potoku (można też sprawdzić kod wyjścia tego procesu w zmiennej $?). Oto przykład użycia polecenia close: open (FILEHANDLE, ">hello.txt") or die ("Nie mogę otworzyć pliku hello.txt"); print FILEHANDLE "Hello!"; close (FILEHANDLE) ; Hello! print — drukowanie do pliku Funkcji print używaliśmy w tej książce już wielokrotnie, ostatnio w poprzednim rozdziale. Funkcja ta drukuje listę danych do pliku o podanym uchwycie: print FILEHANDLE LISTA print LISTA print Funkcji print używa się również w celu dopisywania do pliku danych, jak w przykładzie znanym już z tego rozdziału: open (FILEHANDLE, „>hello.txt") or die („Nie mogę otworzyć pliku hello.txt"); print FILEHANDLE „Hello!"; close (FILEHANDLE); Hello! Funkcja print zwraca wartość true, jeśli jej wykonanie się powiodło. W przypadku, gdy nie zostanie wskazany uchwyt pliku, używa się stdout lub bieżącego kanału wyjściowego (zajrzyj do punktu „select — określanie domyślnego uchwytu pliku wyjścia" w dalszej części tego rozdziału). Jeśli zostanie pominięta również lista, używana jest zawartość zmiennej domyślnej $_. Jako że print jest funkcją list, można użyć jej jak w poprzednim przykładzie, gdzie do pliku przekazujemy dane z tablicy (w tym przypadku ustawiliśmy znak nowego wiersza jako separator rekordu wyjściowego): open (FILEHANDLE, ">array.dat") or die ("Nie mogę otworzyć pliku array.dat"); $, = "\n"; @array = (l, 2, 3); print FILEHANDLE @array; close FILEHANDLE; Tymczasem w poniższy sposób możemy odczytać tablicę z utworzonego właśnie pliku: open (FILEHANDLE, "); close FILEHANDLE; print join(‘, ', @array); l, 2, 3 write — pisanie do pliku Funkcji write, podobnie jak print, można użyć do zapisywania danych do pliku: write FILEHANDLE write WYRAŻENIE write Z write zapoznaliśmy się w poprzednim rozdziale (tam szukaj dalszych szczegółów). Funkcji write używa się do pisania formatowanych rekordów, a nie jako funkcji zapisującej ogólnego przeznaczenia (patrz: print). Oto przykład, w którym zapisujemy sformatowany rekord do pliku format. txt: open (FILEHANDLE, ">format.txt") or die ("Nie mogę otworzyć pliku format.txt"); format FILEHANDLE = @<<<<<<<<<@>>>>>>>>>>>>>>>> $textl $text2 $textl = "Hello"; $text2 = "there!"; write FILEHANDLE; close (FILEHANDLE); Hello there! binmode — ustawienie trybu binarnego Niektóre systemy operacyjne (jak DOS i Windows) rozróżniają tryb binarny i tekstowy przetwarzania plików. W tych systemach znaki nowego wiersza (czyli \n) są automatycznie przekształcane na pary powrotu kursora i nowego wiersza (czyli \r\n) w strumieniu wyjściowym, zaś takie pary na same znaki nowego wiersza w strumieniu wejściowym. Aby zmienić to zachowanie, trzeba użyć trybu binarnego za pomocą funkcji binmode: binmode FILEHANDLE Oto przykład z systemu DOS. Jeśli wypiszemy słowo ze znakiem nowego wiersza do pliku, można zobaczyć, że plik wynikowy tak naprawdę zawiera pary \r\n (w ASCII \0x0d\0x0a), widoczne w przypadku oglądania zawartości pliku w postaci kodów znaków: open (FILEHANDLE, ">data.txt") or die ("Nie mogę otworzyć pliku data.txt"); print FILEHANDLE "Hello\nthere!"; close (FILEHANDLE); C:\>debug data.txt -d 107A:0100 48 65 6C 6C 6F 0D 0A 74 68 65 72 65 21 0D 0A DE Hello..there!... Jeśli użyje się jednak binmode, wynik zawiera jedynie znak nowego wiersza: open (FILEHANDLE, ">data.txt") or die ("Nie mogę otworzyć pliku data.txt"); binmode FILEHANDLE; print FILEHANDLE "Hello\nthere!"; close (FILEHANDLE); C:\>debug data.txt -d 107A:0100 48 65 6C 6C 6F 0A 74 68 65 72 65 21 0F 89 1E DE Hello.there!... Ustawianie buforowania kanału wyjściowego Można wymusić na Perlu czyszczenie buforów wyjściowych po każdym wywołaniu print (lub write). W tym celu należy nadać zmiennej $| niezerowąwartość: $| = 1; Jeśli nie zostanie to zrobione, kanały wyjściowe będą buforowane i zapisywane tylko wtedy, kiedy bufor jest pełny lub kiedy kanał jest zamykany. Automatyczne czyszczenie buforów po każdym zapisie można także wymusić funkcją autof lush: autoflush HANDLE WYRAŻENIE Odczyt plików przekazanych w wierszu poleceń Kiedy w wierszu poleceń zostaną przekazane nazwy pliku lub plików, dane z tych plików są przekazywane programowi. W poniższym przykładzie przekazywane są pliki file.txt oraz file2.txt: %printem file.txt file2.txt Teraz można w pętli odczytać zawartość tych plików i na przykład ją wyświetlić: while (<>) { print; } Oto jest plik! A to jest następny plik! Odczyt danych z uchwytu pliku Wyrażenie , zwracające następny wiersz danych wejściowych, jest szczególnie użyteczne do czytania z otwartego pliku, jak w poniższym przykładzie, gdzie odczytujemy całą zawartość p\ikufile.txt: open (FILEHANDLE, ") { print; } Oto jest plik! Jeśli pominięty zostanie plik, operator odczyta dane ze stdin. read — odczyt danych Funkcji read używa się do odczytu danych ze wskazanego pliku: read FILEHANDLE, SKALAR, DŁUGOŚĆ, OFFSET read FILEHANDLE, SKALAR, DŁUGOŚĆ Funkcja ta próbuje odczytać z filehandle długość bajtów i zapisać te dane w ska-larze. Podanie OFFSETU pozwala odczytywać dane od miejsca innego niż początek pliku. Funkcja zwraca liczbę faktycznie przeczytanych bajtów. Oto przykład, w którym odczytujemy zawartość pliku bajt po bajcie: open (FILEHANDLE, ") { print; } close (FILEHANDLE); text. Funkcji seek często używa się do obsługi plików podzielonych na rekordy o stałej wielkości. Użycie seek umożliwia sięgnięcie do dowolnego rekordu w takim pliku (mówimy tu o dostępie swobodnym, w przeciwieństwie do dostępu sekwencyjnego, kiedy to musimy najpierw przeczytać wszystkie dane poprzedzające interesującą nas daną). Do obsługi plików, zawierających rekordy o stałej długości, używa się funkcji typu pack, vec i unpack. Zagadnienie związane: pack — kompresja danych unpack — rozpakowywanie danych vec — wektor składający się z liczb całkowitych bez znaku tell — określanie położenia w pliku Funkcja tell zwraca bieżące położenie w pliku: tell FILEHANDLE tell Jeśli nie zostanie podany filehandle, tell użyje ostatnio odczytywanego pliku. Oto przykład, w którym za pomocą funkcji seek ustawiamy się w określonym miejscu pliku i następnie położenie — określone za pomocą funkcji tell — pokazujemy: open (FILEHANDLE, ") { print; } rename — zmiana nazwy pliku Funkcji rename używa się do zmiany nazwy pliku: rename STARANAZNA, NOWANAZWA Funkcja ta zwraca wartość true (1), jeśli udało się zmienić nazwę pliku, i false (0), gdy operacja się nie powiodła. unlink — usuwanie plików Do usunięcia pliku lub listy plików używa się funkcji unlink, która symuluje działanie polecenia systemu Unix o tej samej nazwie: unlink LISTA unlink Oto przykład, w którym są usuwane wszystkie pliki z rozszerzeniem .old: print 'Usunięto ', unlink (<*.old>), ' plików.’; Usunięto 98 plików. opendir — otwarcie katalogu Funkcja opendir otwiera katalog i tworzy do niego uchwyt, którego można użyć w funkcjach readdir, telldir, seekdir, rewinddir i closedir: opendir DIRHANDLE, WYRAŻENIE Funkcja zwraca wartość true, jeśli otwarcie się udało, i false w przeciwnej sytuacji. closedir — zamknięcie katalogu Uchwyt katalogu zamyka się funkcją closedir: closedir DIRHANDLE Funkcja zwraca wartość true, jeśli operacja się udała, i false w przeciwnej sytuacji. readdir — odczytanie pozycji katalogu Funkcji readdir używa się do odczytu nazw związanych z katalogiem o podanym uchwycie: readdir DIRHANDLE Oto przykład, w którym wyświetlamy nazwy plików z bieżącego katalogu: opendir (DIRECTORY, ' . ' ) or die "Nie mogę otworzyć bieżącego katalogu."; print join (', ', readdir (DIRECTORY)); closedir DIRECTORY; ., .., T6.PL, Z.PL, P.PL, V.PL, W.PL seekdir — ustawienie się w katalogu Funkcja seekdir pozwala ustawić się w żądanym miejscu w katalogu — otwartym przez opendir i wskazywanym przez DIRHANDLE: seekdir DIRHANDLE, POZYCJA Wartość pozycja musi być wartością zwróconą przez telldir. telldir — określenie położenia w katalogu Funkcja telldir pozwala określić bieżące położenie w katalogu, używane przez readdir: telldir DIRHANDLE rewinddir — ustawienie się na początku katalogu Funkcji rewinddir używa się do ustawienia bieżącego położenia w katalogu dla readdir, na początku tego katalogu: rewinddir DIRHANDLE chdir — zmiana katalogu roboczego Do zmiany katalogu roboczego służy funkcja chdir: chdir WYRAŻENIE Jeśli tylko to możliwe, funkcja ta zmienia katalog bieżący na wskazany przez wyrażenie (w przypadku, gdy wyrażenie zostanie pominięte, użyty zostanie katalog domowy użytkownika). Funkcja zwraca wartość true,'jeśli operacja się udała, i false w przeciwnej sytuacji. Oto przykład, w którym zmieniamy katalog bieżący na katalog o jeden poziom wyższy (w systemach Unix i DOS oznaczany przez ..), a następnie wyświetlamy pliki z nowego katalogu bieżącego: chdir '.. ' ; opendir(DIRECTORY, '.') or die "Nie mogę otworzyć katalogu."; print join (', ', readdir(DIRECTORY)); closedir DIRECTORY; ., .., mail, .alias, .cshrc, .login, .plan, .profile mkdir — utworzenie katalogu Funkcja mkdir pozwala utworzyć nowy katalog: mkdir NAZWA, UPRAWNIENIA Funkcja ta tworzy katalog o podanej nazwie i z podanymi uprawnieniami. Funkcja zwraca wartość true, jeśli operacja się udała, i false w przeciwnej sytuacji (ewentualny błąd jest umieszczany w $!). Oto przykład, w którym tworzymy nowy katalog tmp, po czym wchodzimy do niego i zapisujemy tam plik: mkdir 'tmp', 0744; chdir 'tmp'; open (FILEHANDLE, ">hello.txt") or die ("Nie mogę otworzyć pliku hello.txt"); print FILEHANDLE "Hello!"; close(FILEHANDLE); Wersja Perla na Win 32 ignoruje uprawnienia do katalogów, więc można tu katalogi tworzyć bezproblemowo. rmdir — usunięcie katalogu Funkcji rmdir używa się do usunięcia katalogu (operacja jest możliwa tylko wtedy, gdy katalog ten jest pusty): rmdir NAZWA rmdir Funkcja zwraca wartość true, jeśli operacja się udała, i false w przeciwnej sytuacji (ewentualny błąd umieszczany jest w $!). Jeśli nie zostanie podany katalog, który ma być usunięty, użyta zostanie zawartość zmiennej $_. Część III Programowanie w Perlu Rozdział 13. Moduły wbudowane W skrócie W tym rozdziale przyjrzymy się funkcjom, które znajdują się w Perlu w postaci szeregu modułów. Moduł zawiera kod Perla zapisany zgodnie z pewnymi zasadami, które pozwalają tego kodu używać w innych programach. Użycie modułów Perla Więcej o tworzeniu modułów powiemy w rozdziale 15. Teraz wystarczy wiedzieć, że moduły Perla dają mnóstwo gotowego do użycia kodu, są zapisywane w modułach z rozszerzeniem .pm i do programu można je ładować za pomocą instrukcji use: use Moduł LISTA use Moduł use Moduł WERSJA LISTA use WERSJA Jeśli pierwszy parametr przekazany instrukcji use jest liczbą, Perl zgłasza błąd, chyba że jego wersja jest równa tej liczbie lub większa od niej. W sytuacji, gdy zostanie przekazana lista funkcji, ładowane są jedynie te funkcje. Oto przykład, w którym żądamy załadowania modułu Safe: use Safe; $safecompartment = new Safe; $safecompartment->permit(qw(print)); $result = $safecompartment->reval("print \"Hello!\";"); Hello! Poniżej przedstawiamy sposób załadowania funkcji strf time z modułu POSIX: use POSIX 'strftime'; print strftime "Oto data: %Y.%m.%d\n", localtime; Oto data: 1999.10.30 Czasem potrzebne jest także użycie ogranicznika części pakietu ::, aby nakazać załadowanie tylko części modułu, jak poniżej, gdzie używamy submodułu Copy modułu File: use File::Copy,; copy("file.txt", "file2.txt"); Perl przekształca :: na /, zatem File::Copy tak naprawdę oznacza File/Copy, wobec czego Perl w katalogu biblioteki o nazwie File będzie starał się odnaleźć plik Copy.pm, zawierający potrzebny submoduł. Z instrukcją use można używać nie tylko modułów, ale także dyrektyw kompilatora. Wiele takich dyrektyw już poznaliśmy, na przykład po wstawieniu w programie use strict ' vars ' Perl zażąda deklarowania wszystkich zmiennych globalnych. Istnieje wiele modułów standardowych, a w tym rozdziale przejrzymy większość tych najpopularniejszych. Standardowe moduły Perla zestawiono w tabeli 13.1. Tabela 13.1. Standardowe moduły Perla Moduł Działanie AnyDBM_File Ramówka do obsługi wielu plików DBM. AutoLoader Automatycznie ładuje funkcje. AutoSplit Rozdziela pakiet, aby wspomóc automatyczne ładowanie. Benchmark Uruchamianie kodu do testów wydajności. CPAN Interfejs CPAN. CPAN::FirstTime Tworzy plik konfiguracyjny CPAN. CPAN::Nox Uruchamia CPAN bez skompilowanych rozszerzeń. Carp Ostrzeżenia o błędach. Class::Struct Tworzenie strukturalnych typów danych podobnych do języka C. Config Informacje o konfiguracji Perla. Cwd Ścieżka bieżącego katalogu roboczego. DB_File Operacje na bazach danych Berkeley. Devel::SelfStubber Tworzy bazy samoładujących się modułów. DirHandle Metody tworzenia uchwytów katalogów. DynaLoader Ładuje biblioteki języka C. English Użycie angielskich nazw zmiennych specjalnych. Env Pobieranie zmiennych środowiskowych. Exporter Domyślna metoda importu modułów. ExtUtils::Embed Włączanie Perla do aplikacji C/C++. ExtUtils::Install Instalowanie plików. ExtUtils::Liblist Pobranie bibliotek, które mają być użyte. ExtUtils::MM_OS2 Podmienia standardowe zachowanie ExtUtils::MakeMaker, zgodne z systemem Unix. ExtUtils::MM_Unix Używane przez ExtUtils::MakeMaker. ExtUtils::MM_VMS Podmienia standardowe zachowanie ExtUtils::MakeMaker, zgodne z systemem Unix. ExtUtils::MakeMaker Tworzy rozszerzenia Makef ile. ExtUtils::Manifest Zapisuje plik wynikowy. ExtUtils::Mkbootstrap Tworzy plik bootstrap używany przez DynaLoader. ExtUtils::Mksymlists Zapisuje plik opcji konsolidatora. ExtUtils::testlib Dodaje do @INC katalogi. Fatal Generuje błędy krytyczne. Fcntl Ładuje plik nagłówkowy języka C Fcntl.h. File::Basename Analizuje ścieżkę. File::CheckTree Badanie drzewiastej struktury plików. File::Compare Porównanie plików. File::Copy Kopiowanie plików. File::Find Poruszanie się po drzewie plików. File::Path Tworzenie i usuwanie katalogów. File::stat Interfejs do funkcji stat (). FileCache Umożliwia otwieranie większej liczby plików. FileHandle Metody obsługi uchwytów plików. FindBin Znalezienie katalogu skryptu perłowego. GDBM_File Biblioteka GDBM. Getopt::Long Przetwarzanie opcji wiersza poleceń. Getopt::Std Przetwarzanie przełączników jednoznakowych. I18N::Collate Porównanie 8-bitowych danych skalarnych zgodnie z bieżącymi ustawieniami lokalnymi. IO Ładuje moduły I/O. IO::File Metody obsługi uchwytów plików. IO::Handle Metody uchwytów I/O. IO::Pipe Metody potoków. IO::Seekable Metody obiektów I/O. IO::Select Wybór wywołania systemowego. IO::Socket Komunikacja za pośrednictwem gniazd. IPC::Open2 Umożliwia odczyt i zapis pliku. IPC::Open3 Umożliwia odczyt i zapis pliku, obsługuje błędy. Math::BigFloat Tworzenie liczb zmiennoprzecinkowych dowolnej długości. Math::BigInt Tworzenie liczb całkowitych dowolnej długości. Math::Complex Liczby zespolone. Math::Trig Interfejs funkcji trygonometrycznych dla Math::Complex. NDBM_File Dostęp do NDBM. Net::Ping Wywołuje polecenie ping do serwera internetowego. Net::hostent Interfejs do funkcji gethost*. Net::netent Interfejs do funkcji getnet*. Net::protoent Interfejs do funkcji getproto*. Net::servent Interfejs do funkcji getserv*. Opcode Uniemożliwia stosowanie nazw plików. Pod::Text Przekształca format POD (Plain Old Documentation) na sformatowany tekst ASCII POSIX Interfejs do POSIX, standard IEEE 1003.1. SDBM_File Dostęp do plików SDBM. Safe Uruchamia kod w wydzielonej przestrzeni. Search::Diet Wyszukuje w słowniku klucz. SelectSaver Zapis i odzyskanie uchwytu do pliku. SelfLoader Ładuje funkcje jedynie na żądanie. Shell Wywołuje rozkazy systemowego interpretera poleceń. Socket Ładuje definicje socket.h języka C. Symbol Obsługa symboli Perla. Sys::Hostname Pobiera nazwę komputera „host". Sys::Syslog Interfejs syslog (3). Term::Cap Interfejs terminala. Term::Complete Uzupełnianie słów. Term::ReadLine Interfejs do pakietów odczytu wiersza. Test::Harness Uruchamia skrypty testowe i zapisuje statystykę. Text::Abbrev Tworzy tablicę skrótów. Text::ParseWords Analizowanie tekstu. Text::Soundex Algorytm soundex (porównywanie brzmienia wyrazów języka angielskiego). Text::Tabs Rozwijanie (bądź nie) tabulatorów. Text::Wrap Zawijanie wierszy. Tie::Hash Definicje związanych asocjacji. Tie::RefHash Definicje związanych asocjacji ze wskaźnikami w roli kluczy. Tie::Scalar Definicje związanych skalarów. Tie:: SubstrHash Tworzy asocjacje o stałej wielkości i z ustaloną długością klucza. Time::Local Określa czas na podstawie czasu lokalnego i Greenwich. Time::gmtime Interfejs funkcji gmtime. Time::localtime Interfejs funkcji localtime. Time::tm Używany przez Time::gmtime i Time::localtime UNIVERSAL Klasa bazowa dla wszystkich innych klas. User::grent Interfejs funkcji getpr*. User::pwent Interfejs funkcji getpw*. Dodatkowo w tabeli 13.2 zestawiono standardowe dyrektywy Perla (zaczynają się typowo małymi literami, podczas gdy nazwy modułów na początku mają wielką literę). Oprócz modułów standardowych, na stronach CPAN i innych (jak witryna ActiveState dotycząca Perla na Win32) znajduje się mnóstwo modułów dodatkowych, które obsługuj ą wszystko — od Internetu po użycie Tcl/Tk. Moduły te można pobrać bezpośrednio, ale także można wykorzystać Menedżer pakietów Perla (PPM, Perl Package Manager) czy skrypt Perla (ppm.pl) dostarczany wraz z Perlem do Win32. Oto polecenia dostępne w PPM po podłączeniu się do Internetu: * help — wyświetla pomoc; * install pakiety— ładuje i instaluje wskazane pakiety; * query — pobiera informacje o zainstalowanych pakietach; * quit — kończy działanie PPM; * remove PACKAGES — usuwa wskazane pakiety z systemu; * search — pobiera informacje o wskazanych pakietach; * set — ustawia i wyświetla bieżące opcje; * verify — sprawdza, czy zainstalowane pakiety są aktualne. Tabela 13.2. Standardowe dyrektywy Perla Dyrektywa Znaczenie blib Używanie niezainstalowanej wersji pakietu MakeMaker. diagnostics Realizuje szeroką diagnostykę ostrzeżeń. integer Używa arytmetyki całkowitoliczbowej. less Zabrania użycia wskazanej konstrukcji. lib Decyduje, gdzie Perl ma szukać skryptów. locale Użycie bieżących ustawień lokalnych. ops Ograniczenie stosowania nazw plików. overload Przeciążanie operacji Perla. re Zmiana sposobu zachowania się wyrażeń regularnych. sigtrap Umożliwia obsługę sygnałów. strict Ogranicza użycie potencjalnie niebezpiecznych konstrukcji programistycznych. subs Wymusza deklarowanie procedur. vmsish Użycie zachowań charakterystycznych dla systemu VMS. vars Wymusza deklarowanie zmiennych globalnych. Aby na przykład zainstalować popularny moduł Tk, trzeba połączyć się z Internatem, uruchomić PPM i wpisać install Tk (upraszczając w ten sposób to, co stale było omawiane na grupach dyskusyjnych o Perlu). Moduł Tk obsługuje pakiet narzędzi Tcl/Tk i umożliwia tworzenie w Perlu graficznego interfejsu użytkownika. Moduł ten stał się wśród programistów perłowych wyjątkowo popularny, dlatego — mimo że nie jest to moduł wbudowany w ścisłym tego słowa znaczeniu — przyjrzymy mu się w tym rozdziale, pokazując sposób tworzenia okienek z przyciskami, opcjami radio, menu, listami wyboru i innymi elementami graficznymi Tk (wygląd tych elementów graficznych będzie odmienny w różnych systemach operacyjnych). Po tym wstępie możemy zacząć nasz przegląd modułów Perla. Zagadnienia: Term::Cap — obsługa terminala Modułu Term można użyć do wyświetlania danych na terminalu, na przykład funkcja Tgoto przesuwa kursor we wskazane miejsce. Oto przykład, w którym przesuwamy kursor do 40. kolumny 5. wiersza i wyświetlamy tam słowo „Perl". Użycie funkcji ż"modułów Term i POSIX pozwala nam uzyskać obiekt terminala termcap (modułu posix używamy do określenia szybkości wyjściowej terminala): use POSIX; use Terra::Cap; $termios = POSIX::Termios->new; $termios->getattr; $speed = $termios->getospeed; $tercap = Term::Cap->Tgetent ({TERM => undef, OSPEED => $speed}); Teraz możemy użyć obiektu termcap do wyczyszczenia ekranu za pomocą Tputs i umieszczenia za pomocą Tgoto kursora w żądanym miejscu: use POSIX; use Terra::Cap; $termios = POSIX::Termios->new; $termios->getattr; $speed = $termios->getospeed; $tercap = Term::Cap->Tgetent ({TERM => undef, OSPEED => $speed}); $termcap->Tputs('cl', 1, *STDOUT); $termoap->Tgoto('cm’, 40, 5, *STDOUT); print "Perl\n"; W rezultacie powyższych działań kursor został przesunięty dookreślonego wcześniej punktu (5,40). Opisane funkcje zadziałają w terminalach i ich emulatorach, ale nie będą działały w środowiskach takich, jak okienka DOS-a. Math: duże liczby i liczby urojone Modułu Math można użyć do realizacji działań na dużych liczbach lub liczbach zespolonych. Oto przykład utworzenia dwóch liczb zespolonych (czyli liczb zawierających część rzeczywistą i część urojoną), dodania ich i wyświetlenia wyniku: use Math::Complex; $operandl = Math::Complex->new(l, 2); $operand2 = Math::Complex->new(3,4); $sum = $operandl + $operand2; print "Suma = $sum\n"; Suma = 4 + 6i Z kolei subpakiet BigInt można zastosować do dodawania do siebie dwóch dużych liczb całkowitych (istnieje też BigFloat dla liczb zmiennoprzecinkowych). Zauważ, że liczby te dodajemy jako całkowite, ale Perl nie jest w stanie obsłużyć ich bezpośrednio i przekształca je na wartości zmiennoprzecinkowe: use Math::BigInt; $intl = 123456789123456789; $int2 = 987654321987654321; print "Suma liczb całkowitych = $int1 + $int2, "\n"; Suma liczb całkowitych = l.1111111111111e+018 $bint1 = Math::BigInt->new('123456789123456789'); $bint2 = Math: :Biglnt->new('987654321987654321'); print "Suma liczb całkowitych = ", $bint1->badd($bint2), "\n"; Suma liczb całkowitych = 1.11111111111111110 POSIX: Przenośny interfejs dostępu do systemu operacyjnego Narodowy Instytut Standaryzacji i Technologii i Laboratorium Systemów Komputerowych (NIST/CSL) wraz z innymi firmami i organizacjami stworzyły standard POSIX (Portable Operating System Interface, Przenośny interfejs systemu operacyjnego). POSIX to duża biblioteka funkcji zapisanych w stylu C, obejmująca typowe operacje programistyczne: od podstawowych funkcji matematycznych po złożoną obsługę plików. Moduł POSIX Perla daje dostęp do prawie wszystkich identyfikatorów POSIX 1003.1 — łącznie około 250 funkcji. Moduł POSIX do programu dodaje się za pomocą instrukcji use: use POSIX; # Dodanie całej biblioteki POSIX use POSIX qw(FUNKCJA) ; # Dodanie wybranej funkcji Możemy na przykład użyć funkcji strftime do sformatowania i wyświetlenia aktualnej daty: use POSIX 'strftime'; print strftime "Dzisiaj mamy dzień %d.%m.%Y\n", localtime; Dzisiaj mamy dzień 30.10.1999 Warto przyjrzeć się, co pakiet POSIX zawiera — na przykład umieszczone tam procedury mogą być mniej zależne od używanego systemu, niż sam Perl. Benchmark: testy wydajności Modułu Benchmark używa się do sprawdzania wydajności kodu — zupełnie, jakby mierzyć czas jego wykonania ze stoperem w ręku (trzeba jednak pamiętać, że w systemach wielozadaniowych na czas wykonywania poszczególnych programów może mieć wpływ wiele dodatkowych czynników). W poniższym przykładzie za pomocą obiektów / Benchmark wyliczamy czas wykonania miliona iteracji pętli: use Benchmark; $timestampl = new Benchmark; for ($loop_index = 0; $loop_index < 1_000_000; $loop_index++) { $variablel = 1; } $timestamp2 = new Benchmark; $timedifference = timediff($timestamp2, $timestampl); print "Wykonanie pętli zajęło", timestr($timedifference); Wykonanie pętli zajęło 5 wallclock secs ( 5.65 usr + 0.00 sys = 5. 65 CPU) Time: czas i jego przeliczanie Moduł Time umożliwia konwersję czasu lokalnego lub uniwersalnego czasu Greenwich na sekundy — od początku epoki Uniksa (czyli l stycznia 1970 roku). Oto przykład użycia subpakietu Time::Local do wyliczenia sekund od początku epoki Uniksa do l stycznia 2000 roku: use Time::Local; print timelocal(0, 0, 0, l, l, 2000); 949381200 Carp: zgłaszanie błędów z punktu widzenia wywołującego Instrukcje war n i die zgłaszaj ą tylko wiersz kodu, w którym wystąpił problem, ale instrukcje Carp zostały utworzone tak, aby pozwalały procedurom obsługi błędów działać raczej jako funkcje włączone do wywołującej je procedury. Innymi słowy — programiści używający procedur z tego modułu, zamiast jakiegoś wewnętrznego numeru, zobaczą także sam wiersz, który spowodował błąd. Oto sposób użycia instrukcji Carp: use Carp; carp "To jest ostrzeżenie!"; # wyświetlenie ostrzeżenia croak "To jest błąd!"; # die + komunikat błędu confess "To już tyle czasu!"; # die + ślad stosu locale: ustawienia lokalne Moduł locale jest właściwie dyrektywą włączającą ustawienia lokalne POSIX dla tych operacji, których one dotyczą. Dyrektywa ta wpływa na sposób sortowania czy znak używany jako kropka dziesiętna. Aby wyłączyć zależność od ustawień lokalnych, używa się no locale. Oto przykład sortowania tablicy przy zastosowaniu porządku zależnego od ustawień lokalnych: use locale; @sorted = sort @unsorted; File: operacje plikowe Moduł File zapewnia obsługę plików, na przykład submoduł File: :Copy zawiera dwie funkcje nienależące do standardowej biblioteki Perla: copy (kopiowanie pliku) oraz move (przeniesienie pliku do innego katalogu). Oto przykład kopiowania pliku file.txt pod nazwę file2.txt, a następnie przeniesienia go do katalogu o jeden poziom wyżej w stosunku do katalogu bieżącego: use File::Copy; copy("file.txt", "file2.txt"); move("file2.txt", ".."); Net: dostęp do Internetu Moduł Net zawiera procedury pozwalające na sięgnięcie do Internetu. Prześledźmy przykład, w którym za pomocą submodułu net: : Ping sprawdzamy połączenie ze wskazanym serwerem. Aby użyć Net: : Ping, trzeba utworzyć obiekt ping, a następnie jego metodą ping wysłać pakiet do serwera. Można zastosować protokół TCP, ICMP lub UDP. Oto sposób tworzenia nowego obiektu ping: $obiektping = Net::Ping->new([protokół [, czas_timeout [, bajtów]]}); Wszystkie parametry są opcjonalne; protokół może mieć wartość tcp, udp lub icmp (domyślne jest udp). Istnieje możliwość podania domyślnego czasu zerwania połączenia (czas_timeout) w sekundach oraz liczby wysyłanych bajtów w pakiecie — co najwyżej 1024. Aby wysłać pakiet do serwera, należy wykorzystać metodę ping: obiektping->ping(serwer [, czas_timeout]); Kiedy obiekt ping nie jest już potrzebny, zamyka się go metodą close. Oto przykład wysłania pakietu do zdalnego serwera: use Net::Ping; $pingobject = Net::PIng->new(icmp); if ($pingobject->ping('twój.serwer.com.pl')) {print "Udało się skontaktować z serwerem."}; $pingobject->close() ; Udało się skontaktować z serwerem. Safe: bezpieczne wykonywanie kodu Moduł Safe umożliwia bezpieczne wykonywanie kodu dzięki wydzieleniu odpowiedniej przestrzeni oddzielonej od reszty programu. Metoda permit pozwala wskazać, jakie funkcje w takiej przestrzeni można wywoływać. Z uwagi na to, że kwestie bezpieczeństwa odgrywają dziś coraz większą rolę, to i moduł Safe bardzo się rozrósł — zawiera mnóstwo metod. Oto przykład utworzenia nowej przestrzeni Safe, umożliwienia wykonywania w niej instrukcji print i uruchomienia w niej kodu przy użyciu metody reval: use Safe; $safecompartment = new Safe; $safecomartment->permit(qw(print)); $result = $safecompartment->reval("print \"Hello!\";"); Hello! Tk: zestaw narzędzi Tk Zestawienie Perl/Tk jest wyjątkowo popularne, wobec czego pokażmy tu szereg przykładów zastosowania Tk w Perlu. Dla osób, które używały już Tcl/Tk, większość pokazywanego dalej kodu powinna być jasna (a jeśli tak nie będzie, pomocna okaże się dokumentacja Perl/Tk). Zasada jest prosta: moduł Tk włączamy za pomocą instrukcji use Tk, tworzymy okno główne, a następnie tworzymy i wstawiamy obiekty poszczególnych elementów graficznych, określając ich opcje (na przykład -text). Na końcu wywołujemy funkcję MainLoop, przekazując sterowanie modułowi Tk, i uzyskujemy okno z przyciskami. W następnych podpunktach użyjemy modułu Tk; aby te przykłady zrozumieć, konieczna może być również znajomość Tk. Tk: przycisk i pole tekstowe Przykłady Perl/Tk zaczniemy od programu tworzącego okienko Tk z przyciskiem i polem tekstowym. Kiedy użytkownik kliknie przycisk, program wyświetli w polu wyraz „Hello!", co pokazano na rysunku 13.1: use Tk; $topwindow = MainWindow->new(); $topwindow->Label('-text' => 'Przycisk i pole edycyjne - przykład')->pack(); $topwindow->Button( -text => "Kliknij mnie!", -command => \&display)->pack(-side => "left"); $textl = $topwindow->Text('-width'=> 40, '-height' => 2)->pack(); $textl->bind('', \sdisplay); sub display { $textl->insert('end’, "Hello!"); } MainLoop; Tk: przyciski radio i pole opcji Poniższy przykład Perl/Tk pokazuje wyświetlanie przycisków radio i pól opcji, jak również prezentuje opcję po kliknięciu jej przez użytkownika — rysunek 13.2. use Tk; $topwindow = MainWindow->new(); $topwindow->Label('-text' => 'Przycisk radio i pole opcji - przykład1)->pack(); $topwindow->Radiobutton( -text => "Radio l", -command => sub{ $text1->delete ('l.0', ‘end’); $text1->insert(‘end’, “Kliknąłeś Radio 1”);})->pack(); $topwindow->Radiobutton( -text => "Radio 2", -value=>”0”, -command => sub{ $text1->delete ('l.0', ‘end’); $text1->insert(‘end’, “Kliknąłeś Radio 2”);})->pack(); $topwindow->Checkbutton(‘-text’ => “Opcja 1”, -command => sub{ $text1->delete(‘1.0’, ‘end’); $text1->insert(‘end’, “Kliknąłeś Opcję 1”);})->pack(); $topwindow->Checkbutton(‘-text’ => “Opcja 2”, -command => sub{ $text1->delete(‘1.0’, ‘end’); $text1->insert(‘end’, “Kliknąłeś Opcję 2”);})->pack(); $text1=$topwindow->Text (‘-width’ => 40, ‘-height’ => 2)->pack(); MainLoop; Tk: lista wyboru W tym przykładzie wyświetlimy listę wyboru, a kiedy użytkownik dokona wyboru przez podwójne kliknięcie, wynik wyświetlimy w polu tekstowym, jak na rysunku 13.3. use Tk; $topwindow = MainWindow->new(); $topwindow->Label('-text' => 'Przykład listy wyboru')->pack(); $listboxl = $topwindow->Listbox("-width" => 25, "-height" => 5)->pack(); $listboxl->insert(‘end', "Jabłka", "Banany","Pomarańcze", "Gruszki", "Ananasy"); $listboxl->bind('', \&getfruit); $textl = $topwindow->Text ('-width' => 40, '-height’ => 2)->pack(); sub getfruit ( $fruit = $listboxl->get('active'); $textl->insert('end1, "$fruit"); } MainLoop; Tk: suwak W następnym przykładzie wyświetlimy suwak pakietu Tk i poinformujemy o wartości wybranej przez użytkownika. use Tk; $topwindow = MainWindow->new(); $topwindow->Label('-text' => 'Przykład suwaka')->pack(); $topwindow->Scale('-orient' => 'horizontal', '-from’ => 0, '-to’ => 200, 'tickinterval' => 40, '-label' => 'Wybierz wartość:', '-length' => 200, '-variable' => \$value, '-command' => \&display)->pack(); $textl = $topwindow->Text('-width' => 40, '-height' => 2)->pack(); sub display { $textl->delete('l.0', 'end'); $textl->insert('end', "$value"); } MainLoop; Tk: płótno W tym przykładzie utworzymy i wyświetlimy płótno z pakietu Tk, po czym narysujemy na nim niebieską elipsę — rysunek 13.5. use Tk; $topwindow = MainWindow->new() ; $canvasl = $topwindow->Canvas('-width' => 200, '-height’ => 200)->pack(); $canvasl->create('oval', '50', '50', '160', '160', '-fill' => 'blue'); MainLoop; Tk: menu W następnym przykładzie utworzymy system menu Tk z dwoma menu: Plik i Edycja. Kiedy użytkownik wybierze z menu jakąś opcję, co zaprezentowano na rysunku 13.6, w polu tekstowym pokażemy wybraną opcję — rysunek 13.7. use Tk; my $topwindow = MainWindow->new() ; $menubar =$topwindow->Frame()->pack('-side' => 'top', '-fill1 => 'x'); $filemenu = $menubar->Menubutton('-text' => 'Plik')->pack('-side' => 'left'); $filemenu->command('-label' => 'Otwórz', '-command' => sub ($text->delete('1.0', 'end1); $text->insert('end', "Wybrałeś otwarcie pliku.");)); $filemenu->separator(); $filemenu->command('-label' => 'Koniec', '-command' => sub {exit}); $editmenu = $menubar->Menubutton('-text' => 'Edycja')->pack('-side1 => 'left'); $editmenu->command('-label' => 'Szukaj1, '-command' => sub ($text->delete('1.0', 'end'); $text->insert('end', "Wybrałeś wyszukiwanie.");}); $editmenu->command('-label' => 'Zastąp1, '-command' => sub ($text->delete('l.0', 'end1); $text->insert('end', "Wybrałeś zastępowanie.");}); $topwindow->Label('-text' => 'Przykład użycia menu')->pack(); $text - $topwindow->Text('-width' => 40, '-height1 => 3)->pack(); MainLoop; Tk: Okienka dialogowe Tym razem wyświetlimy okienko dialogowe Tk, kiedy użytkownik kliknie przycisk — rysunek 13.8. Użytkownik może wpisać w pole tekstowe dane, a jeśli potem kliknie przycisk OK, program wyświetli te dane w okienku głównym — rysunek 13.9. use Tk; $topwindow = MainWindow->new (); $dialog = $topwindow->DialogBox(-title => "Okienko dialogowe", -buttons => ["OK", "Anuluj"]); $entry = $dialog->add("Entry", -width => 40)->pack(); $topwindow->Label('-text' => 'Przykład okienka dialogowego')->pack(); $topwindow->Button( -text => "Pokaż okienko dialogowe", -command => \&show)->pack(); $textl = $topwindow->Text ('-width’ => 40, '-height' => 2)->pack(); MainLoop; sub show { $result = $dialog->Show; if ($result eq "OK") { $textl->delete('1.0', 'end'); $textl->insert('end', $entry->get); } } Rozdział 14. Struktury danych W skrócie Najważniejszym mankamentem Perla, występującym aż do wersji 5, jest brak obsługi złożonych struktur danych, nawet wielowymiarowych tablic. Chyba właśnie tego programistom brakowało najbardziej — możliwości użycia w tablicy więcej niż jednego indeksu. Dotąd tablice trzeba było modelować na sposoby w najlepszym razie pokrętne. Konieczne było traktowanie indeksów tablic jako tekstu i łączenie ich w dłuższe zdania, które były używane jako klucze asocjacji — jak w poniższym przykładzie, gdzie tworzymy „tablicę" dwuwymiarową: for $outerloop (0..4) { for $innerloop (0..4) { $array{"$outerloop, $innerloop"} = 1; } } Po utworzeniu takiej struktury danych można sięgać do elementów tej „tablicy", podając indeksy złączone w jeden ciąg znaków: print $array {'0, 0'}; Obecnie w Perlu dodano szeroką obsługę struktur danych, w tym tablice wielowymiarowe, można więc utworzyć następujący kod: for $outerloop (0..4) { for $innerloop (0..4) { $array[$outerloop][$innerloop] = 1; } } print $array[0][0]; 1 Jednak rzecz jest nieco bardziej skomplikowana, niż to się wydaje. Tablice i asocjacje Perla w zasadzie są jednowymiarowe, zatem w powyższym przykładzie utworzyliśmy tablicę wskaźników na inne tablice jednowymiarowe.' Dzięki temu, że istnieje możliwość pominięcia operatorów dereferencji -> między nawiasami, można pisać taki kod, jak powyżej, czyli na przykład Sarray [$outerloop] [innerloop], co jest równoważne Sarray[Souterloop]->[innerloop]. Trzeba jednak pamiętać, że tak naprawdę mamy tu do czynienia z tablicą wskaźników do tablic. Jeśli na przykład uruchomiona zostanie instrukcja print @array, nie pojawią się wcale elementy tablicy dwuwymiarowej, ale tylko wskaźniki do innych tablic jednowymiarowych: ARRAY(0x8a56e4) ARRAY (0x8a578c) ARRAY <0x8a58d0) ARRAY (0x8a5924) ARRAY(0x8a5978) Jako że tablica dwuwymiarowa tak naprawdę jest jednowymiarową tablicą wskaźników na inne tablice jednowymiarowe, można użyć konstruktora tablic anonimowych, [ ]: $array[0] = ["jabłka", "pomarańcze"]; $array[l] = ["asparagus", "kukurydza", "groch"]; $array[2] = ["szynka", "kurczak"]; print $array[1][1]; kukurydza To samo można zrobić również inaczej - w ten sposób inicjalizujemy @array listą wskaźników na tablice: @array = ( ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ); print $array[l][l]; kukurydza Tę tablicę stworzyliśmy, przekazując listę wskaźników na tablice. Jeśli zamiast nawiasów zwykłych zastosujemy nawiasy kwadratowe, w $array zostanie przekazany wskaźnik na anonimową tablicę tablic, do którego można później użyć operatora dereferencji ->: $array = [ ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ] ; print $array->[l][1]; kukurydza Oprócz zapisywania w tablicy wskaźników na inne tablice, czasem można zobaczyć następujący kod, który tworzy tablicę dwuwymiarową: @{$array[0]} = ("jabłka", "pomarańcze"); @{$array[l]} = ("asparagus", "kukurydza", "groch"); @{$array[2]} = ("szynka", "kurczak"); print $array[l][l]; kukurydza Interpretacja zapisu @ {$array [0] } jest dość złożona: Perl „wie", że konstrukcja @{} umożliwia dereferencję wskaźników na tablice, ale jako że $array [0] nie istnieje, Perl automatycznie taki element tworzy i zapisuje tam wskaźnik do tablicy zawierającej elementy z przypisanej listy. Ten sam proces powtarza się w odniesieniu do $array[l] i $array [2] — i tak oto powstaje dwuwymiarowa tablica. Jednak w przypadku stosowania takiego kodu trzeba zachować ostrożność, bo gdyby $array[0] już wcześniej istniała, to niezależnie od tego, na co by wskazywała, zostałaby nadpisana. Po utworzeniu tablicy dwuwymiarowej można sięgać do jej elementów za pomocą indeksów: @array = ( ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"] ); for $outer (0..$#array) { for $inner (0..$#{$array[$outer]}) { print $array[$outer][$inner], " " ; } print "\n"; } jabłka pomarańcze asparagus kukurydza groch szynka kurczak Nadal istnieją techniki obsługi tablic jednowymiarowych, które można wykorzystać do obsługi tablic wielowymiarowych. W następnym przykładzie użyjemy indeksu pętli i metody join do wyświetlenia kolejnych tablic jednowymiarowych (zwróć uwagę na zastosowanie zapisu @ {) do dereferencji wskaźnika na tablicę): @array = ( ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ); for $loopindex (0..$#array) { print join (", ", @{$array[$loopindex]}), "\n"; } jabłka pomarańcze asparagus kukurydza groch szynka kurczak Nie trzeba tu oczywiście stosować indeksu pętli, można użyć bezpośrednio tablicy wskaźników: @array = ( ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ); for $arrayreference (@array) { print join (", ", @{$arrayreference}), "\n"; } jabłka pomarańcze asparagus kukurydza groch szynka kurczak Należy pamiętać, że naprawdę mamy do czynienia ź tablicami tablic i to właśnie stanowi klucz do wszystkich struktur danych omawianych w tym rozdziale. Nie wolno zapominać, że podstawą są wskaźniki, i że nie są to typy podstawowe Perla. Żądanie deklarowania zmiennych? Świetny pomysł! Niejednokrotnie tworzenie struktur danych i obsługa wskaźników są złożonym zadaniem, w którego realizacji może pomóc użycie dyrektywy use strict vars. Załóżmy na przykład, że zapisano kod, ale zamiast () użyto [ ], przypisując wskaźnik na tablicę anonimową $array, zamiast tablicy tablic: use strict vars; $array = [ ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ]; print $array[0][0]; Interpreter Perla wygeneruje w ostatnim wierszu błąd, gdyż niejawnie używamy nieza-deklarowanej zmiennej @array. Błąd ten wynika stąd, że zamiast zewnętrznych nawiasów kwadratowych powinny być użyte zwykłe nawiasy okrągłe, ewentualnie należałoby zastosować $array jako wskaźnik: $array->[0][0] Można tworzyć nie tylko tablice tablic i asocjacje asocjacji, ale również tworzyć konstrukcje mieszane: $array[l][2] # tablica tablic $hash(bigkey}(littlekey) # asocjacja asocjacji $array[3]{key} # tablica asocjacji $hash{key}[4] # asocjacja tablic Wszystkim tym typom przyjrzymy się dokładniej w dalszej części tego rozdziału. Zagadnienia: Złożone rekordy: zapisywanie wskaźników i innych elementów W strukturach danych Perla można przechowywać różnorodne dane, w tym wskaźniki na inne struktury, dzięki czemu można tworzyć złożone struktury połączone wskaźnikami. Tworzenie takich struktur może być bardzo przydatne do tworzenia kopii danych z różnych miejsc, aby zapewnić automatyczną aktualizację danych pierwotnych. Najpierw jako przykład pokażemy zapisywanie różnych typów danych w strukturze, w tym wskaźników — także do procedur. Do zapisywania kopii tablic i asocjacji można użyć odpowiednich tablic i asocjacji anonimowych, ale można też zapisać wskaźniki na już istniejące tablice i asocjacje, co umożliwia użycie gotowych danych: string = "Tutaj mamy jakiś napis."; @array = (l, 2, 3); %hash = ('owoc' => 'jabłka’, 'warzywo' => 'kukurydza'); sub printem { print shift; } $complex = { string => $string, number => 3.1415926, array =>[@array], hash => {%hash}, arrayreference => \@array, hashreference => \%hash, sub => \&printem, anonymoussub => sub {print shift;}, handle => \*STDOUT, }; print $complex->{string}, "\n"; print $complex->{number}, "\n"; print $coraplex->{array}[0], "\n"; print $complex->{hash}{owoc}, "\n"; print ${$complex->{arrayreference}}[0], "\n"; print ${$complex->{hashreference}}{"owoc"}, "\n"; $complex->{sub}->("Wywołanie procedury.\n"); $complex->{anonymoussub}->("Wywołanie procedury anonimowej.\n"); print {$complex->{handle}} "Tekst drukowany do uchwytu pliku.\n"; Tutaj mamy jakiś napis. 3.1415926 1 jabłka 1 jabłka Wywołanie procedury. Wywołanie procedury anonimowej. Tekst drukowany do uchwytu pliku. Deklarowanie tablic tablic Stosując tablice tablic (a nawet tablice tablic tablic i tak dalej), można tworzyć tablice wielowymiarowe, które są bezcenne, kiedy trzeba poindeksować dane względem więcej niż jednego wskaźnika (na przykład tablica studentów ze wskaźnikami: numer studenta i numer egzaminu). Oto najczęściej stosowana metoda tworzenia tablic tablic. Zauważ, że do tworzenia tablicy jednowymiarowej używamy konstruktora tablicy anonimowej: @array = ( ["jabłka", „pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ); Tworzenie tablic tablic na bieżąco Do tworzenia tablic tablic kawałek po kawałku można użyć konstruktora tablic anonimowych, wypełniając tablicę wskaźnikami do innych tablic jednowymiarowych: $array[0] = ["jabłka", "pomarańcze"]; $array[l] = ["asparagus", "kukurydza", "groch"]; $array[2] = ["szynka", "kurczak"]; print $array [1][1]; kukurydza To samo można zrobić, wymuszając na Perlu automatyczne tworzenie wskaźników (zajrzyj na początek tego rozdziału): ${array[0]} = ("jabłka", "pomarańcze"); ${array[l]} = ("asparagus", "kukurydza", "groch"); ${array[2]} = ("szynka", "kurczak"); print $array [1] [1] ; kukurydza Można oczywiście tworzyć i wypełniać tablicę tablic element po elemencie: for $outerloop (0. . 4) { for $innerloop (0..4) { $array [$outerloop] [$innerloop] } } print $array [0] [0] ; 1 Istnieje też możliwość wykorzystania instrukcji push do umieszczania wskaźników w tablicy tablic: for $loopindex (0..4) { push @array, [l, l, l, 1] ; } print $array [0] [0] ; l Oto kolejny przykład, w którym używamy listy zwracanej przez procedurę i konstruktora tablic anonimowych: for $loopindex (0. .4) { $array [$loopindex] = [&zerolist ] ; } sub zerolist { return (0, 0, 0, 0); } print $array[1] [1] ; Można też zawsze dodać do tablicy tablic nowy wiersz: @array = ( ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ); $array[3] = ["makaron z kurczakiem", "chili"]; print $array[3][0]; makaron z kurczakiem Równie dobrze można za pomocą metody push wstawić elementy do już istniejącego wiersza: @array = ( ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ); push @{$array[0]}, "banan"; print $array[0][2]; banan Sięganie do tablic tablic Do tablicy tablic można sięgać element po elemencie: for $outerloop (0. .4) { for $innerloop (0..4) { $array[$outerloop][$innerloop] = 1; } } print $array[0][0]; l Warto też sobie rzecz uprościć i użyć — typowej dla Perla — metody sięgania do tablic jednowymiarowych, jak w poniższym przykładzie, znanym już z początku tego rozdziału. Instrukcję join wykorzystujemy do złączenia tekstu ze wszystkich wierszy tablicy w całość: @array = ( ["jabłka", "pomarańcze"], ["asparagus", "kukurydza", "groch"], ["szynka", "kurczak"], ); for $arrayref ($array) { print join(", ", @{$arrayref}), "\n"; } jabłka pomarańcze asparagus kukurydza groch szynka kurczak Deklarowanie asocjacji asocjacji Asocjacji asocjacji używa się, kiedy jest potrzebny wielopoziomowy tekstowy system informacyjny, na przykład system ekspercki. W takim przypadku używamy tekstu do zagłębiania się na kolejne poziomy struktury danych. Aby od razu utworzyć asocjację asocjacji, można użyć takiej oto deklaracji: %hash = { owoce => { ulubione => "jabłka", 'też dobre' => "pomarańcze", }, warzywa => { ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane' => "rzepa", }, 'mięsa' => { ulubione => "kurczak", 'też dobre' => "wołowina", }, }; print $hash{owoce}{ulubione}; jabłka Jako wartości w parach klucz + wartość przypisywaliśmy następne asocjacje. Tworzenie asocjacji asocjacji na bieżąco Aby utworzyć asocjację asocjacji krok po kroku, dodajemy kolejne asocjacje z innymi kluczami: $hash{owoce} = { ulubione => "jabłka", 'też dobre' => "pomarańcze", }; $hash{warzywa} = { ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane' => "rzepa", }; $hash{'mięsa'} = { ulubione => "kurczak", 'też dobre' => "wołowina", }; print $hash{owoce}{ulubione}; jabłka Oto sposób tworzenia asocjacji przy pomocy konstruktora asocjacji anonimowych {} oraz par klucz + wartość, zwracanych przez procedurę: for $key ("hashl", "hash2", "hash3") { $hash{$key} = {&returnlist}; } sub returnlist { return (keyl => valuel, key2 => value2); } print $hash{hashl}{key2}; value2 Sięganie do asocjacji asocjacji Aby pobrać wartości z asocjacji asocjacji, trzeba się do nich jawnie odwołać: %hash = ( owoce => { ulubione => "jabłka", 'też dobre' => "pomarańcze", }, warzywa => { ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane' => "rzepa", }, }; print $hash{owoce}{'też dobre'}; pomarańcze Używając standardowych technik obsługi asocjacji, możemy zrealizować pętlę po wszystkich elementach asocjacji: %hash = ( owoce => { ulubione => "jabłka", 'też dobre' => "pomarańcze", }, ' warz => { ulubione => "kukurydza", 'też dobre' => "groszek", }, ) ; for $food (keys %hash) { print "$food\t {"; for $key (keys %{$hash{$food}}) { print "'$key' => '$hash{$food}{$key}'"; } print "}\n"; } warz {'ulubione' => 'kukurydza' 'też dobre' => 'groszek'} owoce {'ulubione' => 'jabłka' 'też dobre' => 'pomarańcze'} Do uporządkowania pierwszego poziomu asocjacji należy użyć następującego wyrażenia: owoce {'ulubione' -> 'jabłka' 'też dobre' => 'pomarańcze' } warz {'ulubione' => 'kukurydza' 'też dobre' => 'groszek'} Deklarowanie tablic asocjacji Tablicę asocjacji stosujemy do indeksowania rekordów oznaczonych kluczami (przykład zobaczymy w ostatnim punkcie tego rozdziału, kiedy będziemy tworzyć bufor pierścieniowy). Oto sposób tworzenia tablicy asocjacji od razu w pojedynczej deklaracji: @array = { { ulubione => "jabłka", 'też dobre' => "pomarańcze", }, { ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane' => "rzepa", }, { ulubione => "kurczak", 'też dobre' => "wołowina", }, }; print $$array[0]{ulubione}; jabłka Tworzenie tablic asocjacji na bieżąco Tablice asocjacji można też tworzyć, stopniowo przypisując asocjacje elementom tablicy: @array[0] = { ulubione => "jabłka", 'też dobre’ => "pomarańcze" }; @array[l] = { ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane’ => "rzepa" }; @array[2] = { ulubione => "kurczak", 'też dobre’ => "wołowina" }; print $array[0]{ulubione}; jabłka Tak samo jak w przypadku wszelkich innych tablic, można użyć metody push: push @array { ulubione => "jabłka", 'też dobre' => "pomarańcze" }; push @array { ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane' => "rzepa" }; push @array { ulubione => "kurczak", 'też dobre' => "wołowina" }; print $array[0]{ulubione}; jabłka Oto przykład, w którym odczytujemy pary klucz+wartość i rozbijamy je na tablicę asocjacji: $data[0] = "ulubione:jabłka, też dobre:pomarańcze"; $data[l] = "ulubione:kukurydza, też dobre:groszek, nielubiane:rzepa"; $data[2] = "ulubione:kurczak, też dobre:wołowina"; for $loopindex (0..$$#data) { for $element (split ',', $data [$loopindex]) { ($key, $value) = split ':', $element, $array [$loopindex] {$key} = $value; } print $array [0] {'też dobre'}; pomarańcze Sięganie do tablic asocjacji Do tablic asocjacji sięgamy, używając po prostu indeksu tablicy i klucza wskazanej tym indeksem asocjacji: @array[0] = {ulubione => "jabłka", 'też dobre’ => "pomarańcze"}; @array[l] = {ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane' => "rzepa"}; @array[2] = {ulubione => "kurczak", 'też dobre' => "wołowina"}; print $array[0]{ulubione}; jabłka Oto przykład, w którym wyświetlamy wszystkie elementy tablicy asocjacji, tworząc pętlę po wszystkich elementach: @array[0] = {ulubione => "jabłka", 'też dobre' => "pomarańcze"}; @array[l] = {ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane’ => "rzepa"}; @array[2] = {ulubione => "kurczak", 'też dobre' => "wołowina"}; for $loopindex (0..$#array) { print "array[$loopindex]: {"; for $key (keys %{$array[$loopindex]}) { print "'$key' => '$array[$loopindex]{$key}' "; } print "}\n"; } array[0] = {'ulubione' => 'jabłka' 'też dobre' => 'pomarańcze' }; array[l] = {'ulubione' => 'kukurydza' 'też dobre' => 'groszek' 'nielubiane' => 'rzepa' } array[2] = {'ulubione' => 'kurczak' 'też dobre' => 'wołowina' } Ten sam efekt możemy uzyskać, stosując wskaźniki zamiast indeksu pętli: @array[0] = { ulubione => "jabłka", 'też dobre' => "pomarańcze" }; @array[l] = { ulubione => "kukurydza", 'też dobre' => "groszek", 'nielubiane' => "rzepa" }; @array[2] = { ulubione => "kurczak", 'też dobre' => "wołowina" }; for $hashreference (@array) { print "{"; for $key (sort keys %hashreference) { print "'$key' => '$hashreference->{$key}'"; } print "}\n"; } {'ulubione' => 'jabłka' 'też dobre' => 'pomarańcze' }; {'ulubione' => 'kukurydza' 'też dobre' -> 'groszek' ' nielubiane' =•> 'rzepa' } {'ulubione' => 'kurczak' 'też dobre' -> 'wołowina' } Deklarowanie asocjacji tablic Jeśli są potrzebne dane indeksowane, dostępne przez wartość kluczową, można zastosować asocjację tablic. Spośród wszystkich czterech kombinacji łączenia tablic z asocjacjami właśnie asocjacje tablic są najrzadziej spotykane. Poniższy przykład pokazuje deklarowanie asocjacji tablic w jednym kroku: @hash = ( owoce => ["jabłka", "pomarańcze"], warzywa => ["kukurydza", "groszek", "rzepa"], 'mięso' => ["kurczak", "szynka"], ); print $hash(owoce)[0]; jabłka Tworzenie asocjacji tablic na bieżąco Asocjacje tablic można tworzyć na bieżąco. Trzeba w tym celu zapisywać tablice, które odpowiadają kolejnym kluczom asocjacji, przy pomocy konstruktora tablic anonimowych: $hash{owoce} = ["jabłka", "pomarańcze"]; $hash{warzywa} = ["kukurydza", "groszek", "rzepa"]; $hash{'mięso'} = ["kurczak", "szynka"]; print $hash{owoce}[0]; jabłka Można też użyć instrukcji push: push @{$hash{owoce}}, "jabłka", "pomarańcze"; push @{$hash{warzywa}}, "kukurydza", "groszek", "rzepa"; push @{$hash{'mięso’}}, "kurczak", "szynka"; print $hash{owoce}[0]; jabłka Sięganie do asocjacji tablic Do wskazanego elementu tablicy tablic z asocjacji można sięgnąć w następujący sposób: @hash = ( owoce => ["jabłka", "pomarańcze"], warzywa => ["kukurydza", "groszek", "rzepa"], 'mięso’ => ["kurczak", "szynka"], ); print $hash{owoce}[0]; jabłka Oto przykład, w którym wyświetlamy całą asocjację tablic, używając instrukcji join do przekształcenia tablicy na tekst: @hash = ( owoce => ["jabłka", "pomarańcze"], warzywa => ["kukurydza", "groszek", "rzepa"], 'mięso' => ["kurczak", "szynka"], ); for $key (sort keys %hash) { print "$key:\t[", Join(", ", @{$hash{$key}}), "]\n" } owoce: [jabłka, pomarańcze] mięso: [kurczak, szynka] warzywa: [kukurydza, groszek, rzepa] Listy powiązanych wartości i bufory pierścieniowe Używając struktur danych opisanych w tym rozdziale, bez problemu można utworzyć typowe struktury, takie jak drzewa binarne — gdzie dane zapisuje się węzłach łączonych przez gałęzie — albo listy powiązane. Listy powiązane składają się z danych zapisywanych w elementach, które same też są listami. Każdy element wskazuje na element następny w liście (a w listach powiązanych podwójnie — także na element poprzedni), dzięki czemu można taką listę przeglądać element po elemencie. Często stosowaną postacią listy powiązanej jest bufor pierścieniowy, który powstaje po połączeniu listy w pierścień. Aby używać danych z bufora pierścieniowego, stosuje się dwa indeksy elementów: head i taił. Przy zapisywaniu korzysta się z taił, przy odczycie z head. Kiedy head i taił pokrywają się, bufor jest pusty. Dane odczytuje się i zapisuje przez przesuwanie head i taił, dzięki czemu bufory pierścieniowe efektywnie wykorzystują dostępną pamięć (na przykład w komputerach IBM PC i ich klonach klawisze wciskane przez użytkownika są zapisywane właśnie w buforze pierścieniowym. Kiedy ich liczba przekroczy 15, komputer informuje za pomocą sygnał dźwiękowego o przepełnieniu bufora). Poniżej znajduje się przykład, który pokazuje utworzenie — przy użyciu tablicy asocjacji — bufora pierścieniowego z czterema elementami (można zatem zapisać w nim trzy elementy; zapisanie czwartego spowodowałoby, że head i taił znalazłyby się w tym samym miejscu, który to stan jest nieodróżnialny od pustego bufora). Każdy element bufora (a więc i element tablicy) jest asocjacją z dwoma kluczami: data, który odpowiada danym elementu, oraz next, który jest indeksem tablicy do następnego elementu listy. Oto sposób utworzenia samego bufora i ustawienia head i taił na to samo miejsce, co interpretuje się jako pusty bufor: $buffer[0]{next} = 1; $buffer[0]{data} = 0; $buffer[1]{next} = 2; $buffer[1]{data} = 0; $buffer[2]{next} = 3; $buffer[2]{data} = 0; $buffer[3]{next} = 0; $buffer[3]{data} = 0; $head = 0; $tail = 0; Jeśli jakieś dane mająbyć zapisane, trzeba sprawdzić, czy bufor nie jest już pełny —jeśli jest, zwraca się wartość f alse. W przeciwnej sytuacji nową wartość się dopisuje, przesuwa się taił w przód i zwraca się wartość true: sub store { if ($buffer[$tail]{next} != $head) { # Czy bufor jest pełny ? $buffer[$tail]{data} = shift; $tail = $buffer[$tail]{next}; return 1; } else { return 0; } } Jeśli dane należy pobrać, sprawdza się najpierw, czy bufor nie jest pusty — jeśli jest, zwracana jest wartość undef. W przeciwnym razie zwracana jest wartość wskazywana przez head i taił jest przesuwana w przód: sub retrieve { if ($head != $tail) { # $tail == $head => pusty bufor $data = $buffer[$head]{data}; $head = $buffer[$head]{next}; return $data; } else { return undef; } } Spójrzmy teraz przykład zapisywania wartości w buforze, a potem ich pobierania: store 0; store 1; store 2; store 3; # bufor pełny - wartość niezapisana print retrieve, “\n” print retrieve, “\n” print retrieve, “\n” 0 1 2 Mimo że próbowaliśmy zapisać cztery wartości, to po trzech bufor był już pełny i dlatego czwarta wartość została pominięta. Rozdział 15. Tworzenie pakietów i modułów W skrócie Odrobina prywatności to bardzo ważna rzecz w programowaniu. Perl umożliwia podzielenie programu na częściowo autonomiczne fragmenty, dzięki czemu można nie martwić się o ewentualne szkodliwe oddziaływanie jednych fragmentów programu na inne. Aby program podzielić, używa się pakietów, które tworzą w Perlu przestrzenie nazw. Przestrzeń nazw to fragment programu, w którym zdefiniowano odrębny globalny zasięg identyfikatorów — działa to jak lokalna przestrzeń programistyczna. Tak naprawdę nie istnieje coś takiego, jak zasięg „globalny" w Perlu. Określenie „zasięg globalny" oznacza właściwie zasięg na poziomie pakietu. Przy tworzeniu pakietów można być pewnym, że nie wystąpią kłopoty wynikające z pokrywania się nazw zmiennych i procedur, dzięki czemu kodu z pakietów możemy używać w różnych programach. Oprócz pakietów można też tworzyć moduły, czyli specjalnego rodzaju pakiety, które wyjątkowo łatwo się ładuje i scala z resztą kodu — można też używać klas będących podstawą programowania obiektowego. Pakietami i modułami zajmiemy się w tym rozdziale, a klasami w następnym. Pakiety Kod pakietu można umieścić w odrębnym pliku, szeregu plików, ale można też utworzyć kilka pakietów w jednym pliku. Aby zmienić pakiet (a przy tym przestrzeń nazw), stosuje się instrukcję package. Oto przykład pakietu zapisanego w plikupackagel.pl: package packagel; BEGIN { } sub subroutinel {print "Hello!\n";} return l; END { } Instrukcji package użyliśmy do rozpoczęcia nowego pakietu, packagel. Warto zwrócić uwagę na procedury BEGIN i end: pierwsza z nich zawiera kod inicjujący i jest pierwszą procedurą wywoływaną w pakiecie, natomiast END zawiera kod końcowy i jest wywoływana na końcu. Procedury BEGIN i END są wywoływane niejawnie przez samego Perla — warto zauważyć, że właśnie z uwagi na niejawne użycie tych procedur ich nazwy zapisuje się wielkimi literami. W przypadku takich procedur specjalnych zastosowanie słowa kluczowego sub jest opcjonalne. W przykładowym pakiecie utworzyliśmy procedurę subroutinel, z której będziemy mogli potem skorzystać. Nasz kod pakietu zwraca wartość true (instrukcja return 1), aby wskazać, że kod jest gotów do użycia (często cały ten zapis skraca się i zapisuje się po prostu 1. Jeśli w ostatnim wierszu pakietu lub modułu zostanie podana liczba, jest ona wartością zwracaną przez dany pakiet lub moduł). Aby użyć kodu z naszego pakietu, w programie trzeba zastosować instrukcję reąuire: require 'packagel.pl'; Teraz można odwoływać się do identyfikatorów pakietu packagel, poprzedzając je nazwą pakietu i ogranicznikiem pakietu, czyli dwiema dwukropkami. Kiedyś ogranicznikiem pakietu był pojedynczy cudzysłów, ale zostało to zmienione, aby zachować zgodność z innymi językami programowania: require 'packagel.pl'; packagel::subroutinel(); Hello! Istnieje także możliwość umieszczenia w pakiecie innych identyfikatorów, na przykład zmiennych: package packagel; BEGIN { } $variablel = l; sub subroutinel {print "Hello!\n";} return 1; END { } Aby użyć tej zmiennej w kodzie, możemy skorzystać ze zwykłego przedrostka $: $packagel: :variablel (należy pamiętać, że nie można sięgać do tych zmiennych zadeklarowanych ze słowem kluczowym my, które są lokalne dla modułu): require 'packagel.pl’; packagel::subroutinel(); print $package1::variable1; Hello! 1 Pakietem domyślnym jest main, jeśli zatem nazwa pakietu zostanie pominięta, Perl użyje main, więc zapis $::variablel będzie równoważny zapisowi $main::variablel. Można też automatycznie eksportować do bieżącej przestrzeni nazw takie nazwy, jak na przykład subroutinel — wówczas nie trzeba kwalifikować takiej nazwy procedury nazwą pakietu. Aby z tego mechanizmu skorzystać, konieczne jest użycie modułu. Moduły Moduły to pakiety znajdujące się w pojedynczych plikach. Pliki te mają nazwę taką sa-majak pakiet i rozszerzenie .pm. W Perlu zwyczajowo nazwy modułów zaczyna się 'wielką literą. Kod zawarty w modułach może eksportować swoje symbole do tablicy symboli kodu używającego danego modułu, dzięki czemu zbędne jest kwalifikowanie tak wyeksportowanych symboli nazwą modułu. Załóżmy na przykład, że chcemy utworzyć moduł Module l — zapiszemy go w pliku Modulel.pm, zaś do wyeksportowania procedury subroutinel użyjemy modułu Perla Exporter: package Modulel; BEGIN { use Exporter () ; @ISA = qw (Exporter); @EXPORT = qw(&subroutinel); } sub subroutinel {print "Hello!\n";} return 1; END { } Teraz możemy dodać ten moduł do tworzonego programu (zwykle do włączenia modułu stosuje się instrukcję use). Instrukcja require pozwala załadować moduł w chwili uruchamiania, natomiast use ładuje pakiet natychmiast (domyślne rozszerzenie plików dla use i require to .pm). Oto przykład dodania modułu Modulel do innego progra-mu i wywołania automatycznie wyeksportowanej procedury subroutinel: use Modulel; subroutinel () ; Hello! Warto zapoznać się jeszcze z wieloma innymi sprawami dotyczącymi pakietów Perla: można je w sobie zagnieżdżać, udostępniać symbole do eksportowania (bez eksportowania ich domyślnie), a nawet wywoływać procedurę nieistniejącą, jeśli użyje się procedury autoload. Tymi właśnie zagadnieniami zajmiemy się w dalszej części rozdziału. Zagadnienia: Tworzenie pakietu Aby utworzyć nowy pakiet lub przejść do pakietu, należy użyć instrukcji package: package package PRZESTRZEŃNAZW Instrukcja package powoduje zmianę przestrzeni nazwy, czyli przestrzeni symboli globalnych, na przestrzeń wskazanego pakietu. Jeśli nazwa pakietu nie zostanie podana, to pakiet bieżący nie istnieje. Można zadeklarować w jednym pliku szereg pakietów, można też umieścić jeden pakiet w wielu plikach, ale zwykle jednemu pakietowi odpowiada jeden plik. Poniżej znajduje się przykład, w którym zapisujemy pakiet packagel w pliku packagel.pl. Oto cała zawartość tego pliku (zauważ zwracaną wartość true — czyli l — informującą, że pakiet został poprawnie załadowany): package packagel; sub subroutinel {print "Hello!\n";) return 1; Jeśli nie zostanie podana nazwa pakietu, to znaczy, że nie istnieje pakiet bieżący i trzeba będzie kwalifikować wszystkie symbole nazwami pakietów, do których przynależą. Jest to wymóg silniejszy nawet od tego, który wynika z użycia dyrektywy use str ict, gdyż konieczne jest kwalifikowanie procedur! Aby sięgnąć do danego pakietu z innego, można użyć instrukcji reąuire (domyślnie reąuire zakłada, że żądany plik ma rozszerzenie .pm. Jeśli tak nie jest, trzeba podać pełną nazwę pliku). Instrukcja reąuire dodaje kod z żądanego pliku w chwili uruchamiania, nie kompilacji. Po zażądaniu dostępu do pakietu można się odwoływać do jego symboli, kwalifikując nazwy symboli nazwą pakietu i ogranicznikiem pakietu, : :: require 'packagel.pl’; packakel::subroutinel(); Hello! Pakiety nie muszą mieć odrębnych plików. Efekt taki sam jak powyżej uzyskalibyśmy nawet po umieszczeniu wszystkiego w jednym pliku, gdyż Perl, napotykając na instrukcję package, automatycznie przełącza się do nowej przestrzeni nazw: packagel::subroutinel(); package packagel; sub subroutinel {print "Hello! \n";} Hello! Nie musimy z pakietu packagel zwracać wartości true, gdyż pakiet jest w tym samym pliku, w którym go używamy. Tablica symboli pakietu jest przechowywana w asocjacji o takiej samej nazwie, jak nazwa pakietu, z dołączonym ogranicznikiem pakietu, na przykład packagel: :. Mimo że nowy pakiet oznacza nową przestrzeń nazw, Perl automatycznie umieszcza wszystkie symbole pakietu w głównej tablicy symboli (poza symbolami zaczynającymi się literą lub podkreśleniem) znajdującej się w asocjacji main::. BEGIN — konstruktor pakietu Do inicjalizacji kodu pakietu można użyć procedury BEGIN, która jest uruchamiana w chwili kompilacji kodu Gąszcze przed przeanalizowaniem przez Perla dalszej części pliku). W terminologii obiektowej mówi się, że BEGIN jest konstruktorem pakietu. Ini-cjalizacja pakietu może polegać na przykład na przypisaniu zmiennym wartości, jak poniżej (opcjonalnie słowo kluczowe sub występuje przed begin): package package1; sub BEGIN { $text = "Helło!\n"; } sub subroutine1 {print $text} return l; Kiedy Perl uruchomi już procedurę begin, natychmiast usuwa jaz widoku (co oznacza, że nie można wywołać jej jawnie — może ją wywoływać tylko Perl). Teraz, kiedy zmienna $text została już zainicjalizowana, można odwołać się do pakietu packagel: require 'packagel.pl’; packagel::subroutinel(); Hello! Z uwagi na to, że begin jest wywoływana na samym początku, dobrze jest umieścić w niej prototypy procedur pakietu. Często w procedurze begin umieszcza się dyrektywy, gdyż begin jest uruchamiana przed jakimkolwiek innym kodem, dzięki czemu zawarty w niej kod wpływa na zachowanie się kompilatora. W pakiecie można umieścić wiele procedur BEGIN, a będą one wywoływane w kolejności ich umieszczenia. END — destruktor pakietu Tak jak begin inicjalizuje pakiet, tak z kolei procedura end jest ostatnim wywoływanym fragmentem kodu w pakiecie (jest wywoływana przy kończeniu pracy interpretera Perla), można jej zatem użyć do „sprzątnięcia po sobie" i zamknięcia wszystkich otwartych zasobów. Nie można jednak liczyć, że procedura END zawsze będzie wykonywana, bo jeśli program zakończy swoje działanie w trybie awaryjnym, END nie zostanie wywołana. Procedura END jest nazywana destruktorem pakietu. W poniższym przykładzie procedura ta wyświetla tekst: package packagel; sub BEGIN { $text = "Hello!\n"; } sub subroutinel {print $text} return l; sub END { print "Dziękujemy za użycie pakietu packagel!\n"; } Poniżej przedstawiamy wynik wywołania END: require 'packagel.pl'; packagel::subroutinel(); Hello! Dziękujemy za użycie pakietu packagel! W pliku może istnieć szereg procedur END, wykonywanych w kolejności odwrotnej, niż były definiowane (wobec czego będą odpowiadały kolejnym procedurom begin). W procedurze end zmienna $? zawiera wartość, którą ma zwrócić skrypt, zatem można zmienić wartość tej zmiennej (z tego powodu należy być ostrożnym, stosując w end instrukcje, które automatycznie zmieniają wartość $ ?, na przykład wywołania systemowe). Określanie bieżącego pakietu Nazwę bieżącego pakietu można określić, stosując wbudowany identyfikator _pack-AGE_. Aby na przykład w procedurze subroutinel pakietu packagel wyświetlić nazwę bieżącego pakietu, należy użyć następującego kodu: package packagel; BEGIN { } sub subroutinel {print __PACKAGE__;} return l; END { } Oto wynik wywołania subroutinel: require 'packagel.pl'; packagel: :subroutinel (); packagel Pakiet w wielu plikach Nietrudno się domyślić, jak można utworzyć w jednym pliku wiele pakietów — po prostu używa się instrukcji package tyle razy, ile razy trzeba. Jak jednak podzielić jeden pakiet między wiele plików? Okazuje się, że jest to równie proste — wystarczy użyć instrukcji package deklarującej ten sam pakiet w dwóch plikach czy też większej ilości plików. Załóżmy na przykład, że mamy poniższy kod z definicją procedury hello w plikufilel.pl, a bieżący pakiet to packagel: package packagel; BEGIN {} sub hello{print "Hello!\n";} return l; END { } Możemy też użyć drugiego pliku, file2.pl, w którym zdefiniujemy procedurę hello2, należącą także do pakietu packagel: package packagel; BEGIN {} sub hello2{print "Hello again!\n";} return 1; END {} Teraz można zażądać zarówno pliku filel.pl, jak ifile2.pl, a następnie użyć procedur hello i hello2 z pakietu packagel zdefiniowanego w dwóch plikach: require 'file1.pl’; require 'file2.pl'; packagel::hello (); packagel::hello2 () ; Hello! Hello again! Tworzenie modułów Moduły Perla to po prostu pakiety zdefiniowane w pliku o nazwie identycznej z nazwą pakietu i o rozszerzeniu .pm (takie rozszerzenie upraszcza stosowanie use i require, gdyż instrukcje te zakładają użycie takiego właśnie rozszerzenia). Oto przykład definiowania modułu w pliku Module l.pm: package Modulel; BEGIN { } sub subroutinel {print "Hello! \n"; } return 1; EN { } Moduły mogą eksportować symbole do przestrzeni nazw programu, dzięki czemu nie jest konieczne poprzedzanie tych symboli nazwą modułu i ogranicznikiem (choć można to robić). Dalsze szczegóły związane ze stosowaniem modułów omówimy w następnych punktach. Domyślne eksportowanie symboli z modułów Jeśli użyje się modułu Perla — Exporter, możliwe jest domyślne eksportowanie sym-j? :?_ boli z tworzonego modułu. Przy odwoływaniu się w kodzie do modułu, wywoływana arczy ' jest zdefiniowana w tym module metoda import, decydująca, które symbole będą im-; portowane (metoda to procedura obiektu, a więcej informacji na ten temat znajdziesz w rozdziale 16.). Moduł Exporter może sam przygotować metodę import. Na przykład w module Modulel możemy za pomocą modułu Exporter wyeksportować procedurę subroutinel: package Modulel; BEGIN { use Exporter (); @ISA = qw(Exporter) ; @EXPORT = qw(&subroutinel); } sub subroutinel {print "Hello! \n";} return 1; END {} Tablicy @ISA używa się, aby nakazać Perłowi poszukanie w module Exporter metod, które nie zostaną znalezione w module bieżącym — w szczególności dotyczy to metody import. Jeśli mają zostać wyeksportowane jeszcze jakieś inne symbole, dodaje sieje do tablicy @EXPORT @EXPORT = qw(&subroutinel &subroutine2 &subroutine3 $variablel) Teraz, kiedy nasz moduł ma zostać gdzieś użyty, procedura subroutinel jest automatycznie dodawana do przestrzeni nazw, co oznacza, że można ją wywoływać bez kwalifikowania jej nazwy nazwą modułu: use Modulel; subroutinel() ; Hello! Istnieje też możliwość wskazania symboli, które mogą być eksportowane, ale nie w sposób domyślny. Szczegółowe informacje na ten temat znajdą się w następnym punkcie. Zagadnienie związane: Tworzenie metody klasy Udostępnianie symboli z modułu do eksportu Symbole z modułu można eksportować domyślnie, ale należy z tej możliwości korzystać ostrożnie (przecież podstawowym celem stosowania pakietów i modułów jest „niezaśmiecanie" przestrzeni nazw). Alternatywą jest wskazanie, które symbole mogą być eksportowane z modułu: aby to zrobić, należy je umieścić w tablicy @export_ok i użyć modułu Perla, Exporter: package Modulel; BEGIN { use Exporter(); @ISA = qw(Exporter); @EXPORT_OK = qw(&subroutinel); } sub subroutinel (print "Hello!\n";) return 1; END { } Teraz program może zaimportować procedurę subroutinel, ale nie dzieje się to domyślnie. Oto przykład, jak importuje się subroutinel w innym pakiecie: use Modulel qw(&subroutinel); subroutinel(); Hello! Jeśli w instrukcji use zostanie podana lista importowanych symboli, jak powyżej, żadne symbole nie będą importowane domyślnie. Proces obejmie jedynie symbole wskazane jawnie (jeśli oczywiście istnieją i zostały oznaczone jako dopuszczone do eksportowania). Blokowanie importowania symboli Jeśli moduł nie powinien domyślnie importować żadnych symboli, należy w instrukcji use tuż po nazwie modułu dodać pustą parę nawiasów. W przypadku, gdy na przykład Modulel domyślnie importuje procedurę subroutinel, można ten import wyłączyć następująco: use Modulel; subroutinel (); Undefined subroutine &main::subroutinel called at scriptl.pl line 2. Blokowanie eksportowania symboli Jeśli moduł nie ma eksportować symboli, w przypadku używania modułu Exporter można takie symbole umieścić w tablicy @export_fail. W ramach przykładu utworzymy moduł Uptime.pm, którego można będzie używać w systemach Unix i Windows. Moduł ten eksportuje procedurę uptime, która w systemie Unix po prostu wywołuje polecenie uptime (informujące, ile już czasu działa system) za pomocą operatora odwrotnego apostrofu. W systemie Windows nie istnieje polecenie uptime, zatem powinniśmy zablokować eksportowanie symbolu uptime, jeśli używanym systemem operacyjnym jest Windows (do sprawdzenia stosujemy zmienną $~0, która zawiera nazwę systemu operacyjnego): package Uptime; BEGIN { use Exporter(); @ISA = qw(Exporter); if ($*O ne 'MSWin32') { @EXPORT = qw(&uptime); } else { print "Niestety, Win32 nie obsługuje uptime.\n"; @EXPORT_FAIL = qw(&uptime); } } sub uptime {print 'uptime";} return 1; END { } Poniżej zamieszczamy kod z pliku scriptl.pl, gdzie używamy procedury uptime z modułu Uptime: use Uptime; uptime () ; W systemie Unix wynik będzie następujący: 2:45pm up 44 days, 20:32, 15 users, load average: 2.21, 1.48, 0.93 Z kolei w systemie Windows wynik będzie taki: Niestety, Win32 nie obsługuje uptime. Undefined subroutine &main::uptime called at scriptl.pl line 2. Eksportowanie bez metody import Kiedy używa się modułu, wywoływana jest metoda import tego modułu. Metoda ta importuje symbole wyeksportowane przez moduł. Niektóre moduły zawierają własną metodę import, co oznacza, że metoda związana z modułem Exporter Perla nie zostanie wywołana, choć można skorzystać z eksportu modułu Exporter przez wywołanie metody export_to_level. Zwykle metodę export_to_level wykorzystuje się we własnej metodzie import. Oto przykład eksportowania zmiennej $variablel z modułu Modulel przy pomocy własnej metody import: package Modulel; BEGIN { } use Exporter(); @ISA = qw(Exporter); @EXPORT = qw ($variablel); $variablel = 100; sub import { print "Importowanie\n"; Modulel->export_to_level(1, @EXPORT); } return 1; END { } Zakres działania naszej metody import jest właściwie niewielki: wyświetla ona tekst „Importowanie" i eksportuje zmienną $variablel. W tym przypadku eksportujemy symbole do jednego poziomu zagłębienia względem modułu wywołującego, zatem procedurze export_to_level przekazano wartość l oraz tablicę zawierającą eksportowane symbole. Teraz modułu Modulel można użyć tam, gdzie jest potrzebny, a $variablel będzie wyeksportowana automatycznie: use Modulel; print "\$variable1 = “,$variablel; Importowanie $variablel =100 Submoduły zagnieżdżone Jak widzieliśmy już w rozdziale 13., moduły mogą być zagnieżdżane, jak na przykład Term::Cap — w tym przypadku moduł Cap jest submodułem modułu Term. Moduły nie są zagnieżdżane dosłownie, to znaczy wewnątrz modułu Term nie zapisuje się modułu Cap, lecz submoduł umieszcza się w katalogu wewnątrz katalogu jego modułu nadrzędnego. Perl podczas szukania modułów traktuje ograniczniki modułu :: jako ograniczniki katalogu, wobec czego Modulel::Codel jest przekształcane na Modulel/Codel w systemie Unix lub Modulel\Codel w Windows. Przyjrzyjmy się przykładowi. W tym przypadku utworzymy moduł Modulel: :Codei i użyjemy procedury subroutinel z tego modułu. Utworzymy nowy katalog, Modulel i umieścimy w nim plik Codel.pm. Katalog musi znajdować się oczywiście w ścieżce — na przykład Modulel może być podkatalogiem katalogu bieżącego lub podkatalogu lib katalogu Perla (tam zwykle umieszcza się moduły): package Modulel::Codel; BEGIN { use Exporter(); @ISA = qw(Exporter); @EXPORT = qw(&subroutinel); } sub subroutinel (print "Hello!\n";) return 1; END { } Szczególnie istotny jest sposób zadeklarowania nazwy modułu w instrukcji package — Modulel::Codel zamiast zwykłego Code (taka jest właśnie nazwa modułu). Perl wcale nie analizuje kolejnych poziomów w takich nazwach. Teraz można już swobodnie używać procedury subroutinel z modułu Modulel::Codel: use Modulel::Codel; subroutinel(); Hello! Zagadnienie związane: Term::Cap — obsługa terminala Sprawdzanie numeru wersji modułu Teraz możemy już tworzyć moduły przeznaczone do wykorzystania przez innych programistów. Jak jednak zweryfikować, czy będą oni używali właściwej wersji tych modułów? Kontrolę wersji zapewnia moduł Exporter, a wystarczy po prostu ustawić wartość zmiennej $version, jak poniżej, gdzie modułowi Modulel przypisaliśmy wersję 1.00: package Modulel; BEGIN { } use Exporter(); @ISA = qw (Exporter); @EXPORT = qw ($variablel); $VERSION = 1.00; return 1; END { } Teraz, kiedy ktoś używa modułu Module l, może sprawdzić za pomocą procedury require_version dostępną wersję. Jeśli okaże się ona niezgodna z wymaganą wersją, pojawi się informacja o błędzie: use Modulel (); Modulel->require_version(2.00); Modulel 2 required--this is only version l (Modulel.pm) at usem.pl line 2 Automatyczne ładowanie procedur Jeśli spróbujemy wywołać nieistniejącą procedurę, zgłaszany jest błąd, chyba że zostanie zdefiniowana procedura autoload. Owa procedura jest wywoływana przy próbie uruchomienia procedury nieistniejącej, zaś nazwa brakującej procedury jest umieszczana w zmiennej $autoload, a przekazane jej parametry znajdą się w tablicy @_. Zwykle wywoływana procedura istnieje, ale jest w module, który nie został jeszcze załadowany (dlatego proces ten nazywa się automatycznym ładowaniem procedur). Jednak procedura może naprawdę nie istnieć, wtedy procedura autoload musi na ten fakt odpowiednio zareagować. Można na przykład użyć autoload do udostępnienia programistom poleceń systemowych przez umieszczenie wywoływanych poleceń i ich parametrów w odwrotnych apostrofach. Oto przykład, Autoload.pm, w którym używamy procedury AUTOLOAD do wyświetlenia nazwy i parametrów wywołanej nieistniejącej procedury. Warto zauważyć, że procedura autoload jest eksportowana z modułu: package Autoload; BEGIN { use Exporter () ; @ISA = qw(Exporter); @EKPORT = qw(&AUTOLOAD); } sub AUTOLOAD () { my $subroutine = $AUTOLOAD; $subroutine =~ s/.*:://; print "Wywołana została procedura $subroutine z parametrami: ", join(", ", @_); } } return 1; END { } Nazwa procedury, znajdująca się w zmiennej $AUTOLOAD, jest w pełni kwalifikowana. Jeśli na przykład użylibyśmy poniższego kodu do wywołania nieistniejącej procedury printern, zmienna $AUTOLOAD zawierałaby main : : printem: use Autoload; printem (l, 2, 3); W Autoload.pm odrzucamy wszystko poza samą nazwą wywoływanej procedury, zatem efekt wywołania printem będzie następujący: Wywołana została procedura printem z parametrami: l, 2, 3 Teraz już wiadomo, jak procedura została wywołana i z jakimi parametrami, można zatem załadować potrzebny moduł, który zawiera tę procedurę, lub zasymulować działanie tej procedury bezpośrednio w autoload. AutoLoader i SelfLoader Kod programu można rozbić na moduły, jeśli nie powinien on być od razu cały kompilowany i ładowany. Jednym ze sposobów wykonania tego jest użycie modułów AutoLoader i AutoSplit. Aby zastosować AutoSplit, przed procedurami tworzonego modułu umieszcza się napis _end_, dzięki czemu zostaną one pominięte przez kompilator, a następnie używa się metody autosplit modułu AutoSplit. Moduł AutoSplit rozdziela procedury modułu na pliki z rozszerzeniem .al i umieszcza je w podkatalogach katalogu auto. Jeśli na przykład mamy procedurę subl, jej kod zostanie zapisany w pliku auto/subl.al. Moduł AutoSplit tworzy także indeks o nazwie autosplit.ix. Aby używać procedur z rozbitego w powyższy sposób modułu, można stosować domyślną metodę modułu AutoLoader, czyli AUTOLOAD: use AutoLoader 'AUTOLOAD’; Teraz, kiedy nie będzie można odnaleźć wywołanej procedury, moduł AutoLoader spróbuje ją odszukać w autoload.ix, a jeśli ją znajdzie, procedura ta zostanie załadowana i skompilowana. Do ładowania i kompilowania procedur w miarę potrzeb można też użyć modułu SelfLoader. Jego zastosowanie wymaga umieszczenia definicji procedur po napisie _DATA_ (nie _END_), dzięki czemu kompilator je pominie. W sytuacji, gdy wspomniane procedury są wywoływane, moduł SelfLoader je kompiluje i ładuje. Oto przykład wykorzystania modułu SelfLoader do obsługi procedury subroutinel z modułu Modulel: package Modulel; BEGIN { use Exporter(); use SelfLoader () ; @ISA = qw(Exporter SelfLoader); @EXPORT = qw(&subroutinel); } return l; END { } __DATA__ sub subroutine1 {print "Hello!\n)};} Poniżej przedstawiamy sposób odwołania się do procedury subroutinel modułu Modulel z innego modułu. Warto przy tym pamiętać, że subroutinel nie jest ładowana ani kompilowana, póki nie zostanie wywołana: use Module1; subroutinel () ; Hello! Rozdział 16. Klasy i obiekty W skrócie Programowanie obiektowe jest osobną techniką programowania, która umożliwia wcielenie w życie słynnego motta programistycznego: dziel i rządź. Cały pomysł polega na enkapsulacji danych i procedur (nazywanych metodami) w obiekty, które są półautonomiczne, natomiast dane i metody wewnętrzne są ukrywane w obiekcie, dzięki czemu nie są umieszczane w ogólnie dostępnej przestrzeni nazw. Obiekt może współpracować z resztą programu za pośrednictwem jasno określonego interfejsu składającego się z metod publicznych (czyli dostępnych na zewnątrz). W Perlu programowanie obiektowe jest w znacznym stopniu nieformalne — niemalże wszystko trzeba wykonać samodzielnie. Programowanie obiektowe w Perlu obraca się w kręgu kilku pojęć kluczowych: klas, obiektów, metod i dziedziczenia. Poniżej wyjaśniamy znaczenie tych terminów: * Klasa to pakiet, który może udostępniać metody. * Metoda to procedura wbudowana w klasę lub obiekt. Metoda jako pierwszy parametr otrzymuje wskaźnik na obiekt lub nazwę klasy. * Obiekt to element wskazywany wskaźnikiem, jednak w przeciwieństwie do innych wskaźników, w tym przypadku wiadomo, do jakiej klasy należy wskazywany element. Obiekty tworzy się na podstawie klas. * Dziedziczenie to proces wywodzenia jednej klasy, nazywanej klasą pochodną, z innej klasy, bazowej. W klasie pochodnej są dostępne metody klasy bazowej. Wszystkie powyższe pojęcia w programowaniu obiektowym są bardzo ważne, więc omówimy je teraz dokładniej. Klasy Klasa w języku Perl to po prostu pakiet udostępniający' innym fragmentom programu metody (metoda to procedura powiązana z obiektem lub klasą). W programowaniu obiektowym (OOP, object oriented programming) klasy stanowią pewnego rodzaju szablony obiektów. Jeśli zatem o klasie myśleć jako o foremce do wycinania ciastek danego kształtu, to ciastka będą obiektami. Klasę można uważać za „typ" obiektu (na tyle, na ile jest to słuszne w językach z tak swobodną obsługą typów, jak Perl) — do utworzenia obiektu używa się klasy, a następnie stosuje się metody tego obiektu. Do utworzenia obiektu używa się konstruktora klasy, którym zwykle jest metoda new. Konstruktor zwraca wskaźnik na nowo utworzony obiekt. Wewnętrznie konstruktor używa funkcji Perla bless do ustanowienia połączenia między wskaźnikiem (zwykle — wskaźnikiem na dane wewnątrz nowego obiektu) i klasą — tak właśnie tworzony jest obiekt. Przypominamy, że obiekt to dane wskazywane przez wskaźnik, który „wie", do jakiej klasy te dane należą. Oto przykład klasy, Classl, obsługującej konstruktor o nazwie new. W tym konstruktorze tworzymy wskaźnik na anonimową asocjację zawierającą dane obiektu (danych nie trzeba przechowywać akurat w asocjacjach, można użyć tablicy lub nawet skalara). Następnie asocjacja jest wiązana z bieżącą klasą, po czym w końcu zwraca wskaźnik dzięki instrukcji return: package Classl; sub new { my $self = {}; bless ($self); return $self; } return 1; W ten sposób wyczerpaliśmy temat klas w Perlu. Teraz warto zastanowić się, jak tworzyć obiekty takich klas. Obiekty W Perlu obiekt jest nazywany instancją klasy, a procedury obiektu to metody instancji lub po prostu metody. Obiekty oprócz wbudowanych w nie procedur mogą zawierać dane nazywane danymi instancji lub danymi obiektu. Dane wspólne dla wszystkich obiektów danej klasy to dane klasy. W celu utworzenia obiektu wywołuje się konstruktor klasy — zwykle o nazwie new. Oto przykład utworzenia obiektu przygotowanej wcześniej klasy Classl: use Classl; my $objectl = Classl->new(); Obiekt ten nie będzie zbyt przydatny, gdyż nie zawiera danych obiektu ani metod najmniej na razie. Metody Kiedy mamy już obiekt z metodami, można tych metod używać tak, jak to pokazano poniżej — metoda calculate wykonuje na wartościach Soperandl i $operand2 pewne obliczenia i wynik zapisuje w $result: $result = $objectl->calculate($operandl, $operand2); Perl zawiera dwa rodzaje metod: metody klas oraz metody obiektów. Metody obiektów, takie jak choćby calculate, są wywoływane dla obiektów i jako pierwszy parametr otrzymuj ą wskaźnik na obiekt. Z kolei metody klas wywoływane sadła klas i jako pierwszy parametr otrzymują nazwę klasy. Na przykład konstruktor new z poprzedniego punktu jest metodą klasy: my $objectl = Classl->new(); Obiekty mogą zawierać też dane, które są bezpośrednio w tych obiektach dostępne. Nawet jeśli dane zapiszemy w anonimowej asocjacji klasy ciassl z kluczem data, do danych tych możemy sięgnąć w sposób następujący: my $data = $objectl->(DATA}; Jednak w Perlu dane obiektów zwykle udostępnia się jedynie za pośrednictwem metod dostępu, co oznacza, że zamiast sięgać do danych bezpośrednio, używa się do tego metod takich, jak na przykład getdata: my $data = $objectl->getdata(); Takie użycie metod dostępu pozwala kontrolować, kto sięga do danych obiektu i co z nimi robi — w ten sposób można być pewnym, że program na przykład nie przypisze wartości uważanych za nieprawidłowe. Tak więc metody stosuje się do definiowania interfejsu obiektu, za pośrednictwem którego obiekt jest dostępny dla reszty programu. Wprawdzie metody to procedury pakietu, ale mimo to nie eksportuje się ich. Do metod trzeba się odwoływać, podając pełny wskaźnik obiektu lub nazwę klasy. Jest jeszcze jedno pojęcie, które należy poznać w związku z programowaniem obiektowym, zanim zajmiemy się kodem — dziedziczenie. Dziedziczenie Dziedziczenie pozwala wyprowadzić z istniejącej klasy nową klasę, która odziedziczy metody i dane klasy pierwotnej. Do klasy pochodnej można dodawać nowe metody, które rozszerzą jej możliwości. Jeśli na przykład mamy klasę pojazd, możemy na jej bazie utworzyć nową klasę samochód, zawierającą z kolei nową metodę klakson, która będzie wypisywać (po jej wywołaniu) „biiiiip". W ten sposób utworzymy nową klasę zawierającą dodatkową metodę, która nie istnieje w klasie pierwotnej. O dziedziczeniu jeszcze będziemy mówili w tym rozdziale. Teraz, kiedy znamy już podstawowe pojęcia programowania obiektowego, możemy zająć się szczegółami. Zagadnienia: Tworzenie klasy Jak tworzy się w Perlu klasę? Używa się po prostu pakietu, jak w poniższym przykładzie, gdzie tworzymy klasę classl: package Classl; return 1; I to już właśnie jest klasa. Nie ma powodu być zaskoczonym. Klasa to po prostu pakiet, choć jednak zwykle klasy mają metody (w tym jedną bardzo ważną metodę, a mianowicie konstruktor, który pozwala tworzyć nowe obiekty). Tworzenie konstruktora Konstruktory Perla są po prostu metodami o nazwie new, zwracającymi wskaźnik na obiekt danej klasy. Oto przykład konstruktora tworzącego wskaźnik na asocjację anonimową, w której można zapisywać dane, wiązać wskaźnik z bieżącą klasą i zwracać wskaźnik na nowy obiekt: package Classl; sub new { my $self = {}; bless($self); return $self; } return 1; W następnym punkcie omówimy zastosowanie konstruktora do tworzenia obiektu. Tworzenie obiektu Aby utworzyć nowy obiekt danej klasy, wywołuje się konstruktor tej klasy, zwracający wskaźnik na nowy obiekt: package Classl; sub new { my $self = {}; bless ($self); return $self; } return l; use Classl; my $objectl = Classl->new(); Tak więc utworzyliśmy obiekt, jednak takie obiekty nie są zanadto przydatne, jeśli nie zawierają metod. Tworzenie metody klasy Istnieją dwa rodzaje metod: metody klas oraz metody obiektów. Metody klas wywołuje się przez nazwę klasy, zaś metody obiektów przez wskaźnik obiektu. Przy wywoływaniu metody klasy jako pierwszy parametr przekazuje się nazwę klasy. Konstruktory zwykle są metodami klas — oto przykład, w którym wyświetlamy nazwę klasy nowego obiektu w trakcie jego tworzenia: package Classl; sub new { $class = shift; print "Tworzony jest nowy obiekt klasy $class.”; my $self = { } ; bless ($self); return $self; } return 1; use Classl; my $objectl = Classl->new(); Tworzony jest nowy obiekt klasy Classl. Tworzenie instancji metody Przy wywoływaniu metody obiektu (czyli instancji klasy) metodzie tej jako pierwszy parametr przekazuje się wskaźnik na obiekt. Za pomocą tego wskaźnika można sięgnąć do wewnętrznych danych i metod obiektu. Oto przykład, w którym tworzymy metodę obiektu, którą nazwiemy data. Pierwszym przekazywanym jej parametrem jest wskaźnik na sam obiekt — wskaźnika tego użyjemy do zapisania danych w anonimowej asocjacji obiektu (o ile wcześniej otrzymaliśmy jakieś dane do zapisania), a następnie zwracamy wartość zapisanych danych: package Classl; sub new { my $type = { } ; bless($type); return $type; } sub data { my $self = shift; if (@_) {$self->{DATA} = shift;} return $self->{DATA}; } return l; Zapoznajmy się ze sposobem użycia nowej metody obiektu: use Classl; my $objectl = Classl->new(); $objectl->data("Hello!"); print "Oto tekst zawarty w obiekcie: „, $objectl->data; Oto tekst zawarty w obiekcie: Hello! Wywoływanie metody Metody można w Perlu wywoływać na dwa sposoby. Pierwszą z nich, czyli użycie operatora ->, już widzieliśmy — na przykład w poniższym przykładzie w konstruktorze wywołujemy metodę data jako parametr, podając jej liczbę zero: package Classl; sub new { my $self = (); bless($self); $self->data(0); return $self; } sub data { my $self = shift; if (@_) ($self->{DATA} return $self->{DATA}; ) return 1; Tę samą metodę można wywołać, korzystając także z innej składni. Zwróć uwagę na przekazanie wskaźnika na bieżący obiekt jako pierwszego parametru: sub new { my $self = {}; bless($self); data($self, 0) ; return $self; } Tworzenie zmiennej instancji Dane zapisywane w obiekcie nazywane s^danymi instancji, a zmienne związane z obiektem nazywane są zmiennymi instancji. Jednym ze sposobów tworzenia zmiennych instancji jest utworzenie w obiekcie anonimowej asocjacji i zapisanie w niej danych (można też użyć innych konstrukcji, takich jak tablice czy skalary). Oto przykład zapisywania imienia osoby z kluczem name: package Classl; sub new { my $self = {}; $self->{NAME} = "Christine"; bless($self); return $self; } return 1; Teraz przy tworzeniu obiektów danej klasy można odwoływać się do danych obiektu następująco: use Classl; my $objectl = Classl->new(); print "Imię osoby brzmi ", $objectl->{NAME}, "\n"; Imię osoby brzmi Christine Jednak, jak to wspomniano już na początku tego rozdziału, w Perlu zwykle ukrywa się dane, udostępniając je jedynie przez metody dostępu, co oznacza, że danych nie tworzy się ani nie odczytuje bezpośrednio, lecz korzysta się z metod pośrednich —jak get-data do pobierania bieżącej wartości. Tworzenie metod i zmiennych prywatnych Wprawdzie wiele języków obiektowych obsługuje metody i zmienne prywatne (czyli metody wewnętrzne obiektu czy klasy, niedostępne na zewnątrz), ale w Perlu ani takie metody, ani takie zmienne nie są obsługiwane bezpośrednio. Do ograniczenia widoczności symboli do pakietu można oczywiście użyć deklaracji ze słowem kluczowym my. Perl pozwala jednak deklarować metody i zmienne jako prywatne: trzeba tu skorzystać z pewnych konwencji tego języka, a mianowicie należy je poprzedzić podkreśleniem, _. W przeciwieństwie do języków takich jak C++ nie oznacza to, że do obiektów i zmiennych prywatnych obiektu nie można się dostać. Po prostu przyjmuje się założenie, że jeśli identyfikatory zaczynają się od podkreślenia, nie należy używać ich bezpośrednio, gdyż w zamyśle projektanta mają być prywatne. Oto przykład, w którym metoda publiczna sum używa metody prywatnej _add: package Classl; sub new { my $type = {}; $type->{OPERANDl} = 2; $type->{OPERAND2} = 2; bless($type); return $type; } sub sum { my $self = shift; my $temp = _add ($self->{OPERANDl), $self->{OPERAND2}); return $temp; } sub _add {return shift() + shift();} return l; Poniżej przedstawiamy rezultat zastosowania metody sum: use Classl; my $objectl = Classl->new(); print "Oto jest suma: ", $objectl->sum; Oto jest suma: 4 Tworzenie zmiennej klasy Widzieliśmy już tworzenie instancji zmiennych pozwalających przechowywać dane obiektów, ale można też zapisywać dane klasy. Kiedy zmienna ograniczona leksykalnie zostanie zadeklarowana w klasie jako globalna, będzie ona dostępna we wszystkich obiektach danej klasy. Oto przykład, w którym będziemy zliczać obiekty danej klasy, zapisując uzyskaną liczbę w zmiennej klasy $total (zmienna ta będzie zawierać tę samą wartość dla wszystkich obiektów danej klasy). Za każdym razem, gdy tworzony jest nowy obiekt, zwiększamy wartość $total: package Cdata; my $total; sub new { $self = (); $total++; return bless $self; } sub gettotal{return $total;} return 1; Utworzyliśmy też metodę gettotal, która będzie zwracała wartość Stotal, a metody tej możemy użyć do wyświetlenia liczby już utworzonych obiektów klasy Cdata: use Cdata; $objectl = Cdata->new; print "Aktualna liczba obiektów: „, $object1->gettotal, „\n”; $object2 = Cdata->new; print "Aktualna liczba obiektów: „, $object1->gettotal, „\n”; $objectB = Cdat.a->new; print "Aktualna liczba obiektów: „, $object1->gettotal, „\n”; Aktualna liczba obiektów: l Aktualna liczba obiektów: 2 Aktualna liczba obiektów: 3 Jak widać, dane klasy pozwalaj ą koordynować wszystkie obiekty tej klasy, co jest przydatne do tworzenia liczników, inicjalizacji danych i tak dalej. Tworzenie destruktora Konstruktorów używa się do tworzenia obiektów, natomiast destruktory zawierają kod uruchamiany przy usuwaniu obiektów (na przykład przy wyjściu poza ich zakres czy przy kończeniu pracy interpretera). W przeciwieństwie do konstruktorów, destruktory mają w Perlu jednoznacznie określoną nazwę: destroy. Tak jak inne funkcje wywoływane niejawnie, tak i nazwę destroy zapisuje się wielkimi literami. destroy jest wywoływana przez Perla, a więc nie można jej wywołać samemu jawnie. W poniższym przykładzie dodajemy destruktor wyświetlający komunikat: package Classl; sub new { my $self = {}; bless($self); return $self; } sub DESTROY{print "Obiekt jest usuwany!"} return 1; Teraz, kiedy usuwany będzie obiekt klasy Classl, zostanie wyświetlony komunikat — w tym przypadku usuwanie obiektu jest spowodowane zamykaniem programu: use Classl; my $objectl = Classl->new(); exit; Obiekt jest usuwany! Dziedziczenie klas Jednym z najważniejszych aspektów programowania obiektowego jest dziedziczenie, gdyż to ono umożliwia tworzenie bibliotek klas i dostosowywanie tych klas do swoich potrzeb — przez tworzenie klas pochodnych, które dziedziczą po klasach bazowych całą funkcjonalność. Jak to już opisano na początku tego rozdziału, klasa pochodna może dziedziczyć po innej klasie nazywanej klasą bazową. Klasa pochodna ma dostęp do wszystkich metod i danych klasy bazowej (w przeciwieństwie do innych języków obiektowych nie można deklarować w klasie sekcji prywatnych ani chronionych). W poniższym przykładzie używamy klasy Classl jako klasy bazowej dla Class2. Zwróć uwagę na metodę get-text klasy Classl, dostępną później w klasie Class2: package Classl; sub new { my $self – {}; bless($self) ; return $self; } sub gettext {return "Hello!\n";} return 1; Oto klasa Class2 dziedzicząca po klasie Classl. Dziedziczenie polega na użyciu instrukcji use Classl i umieszczenie Classl w tablicy @ISA (tablicę tę interpretuje się tak, że Class2 znajduje się w relacji ,jest przykładem" z Classl): package Class2; usa Classl; @ISA = qw(Classl); sub new { my $self = Classl->new; bless($self); return $self; } return 1; Jeśli Perl nie będzie w stanie znaleźć metody lub zmiennej w klasie, przeszuka klasy z tablicy @ISA w kolejności ich umieszczenia, gdyż właśnie ta tablica służy do realizacji dziedziczenia w Perlu. Teraz możemy zadeklarować obiekt klasy Class2 i użyć w nim metody gettext odziedziczonej po ciassl: use Class2; my $objectl = Class2->new(); print "Obiekt mówi: ", $objectl->gettext; Obiekt mówi: Hello! W ten sposób klasa Class2 odziedziczyła po klasie ciassl metodę gettext. Być może zauważyłeś w tym przykładzie pewną istotną rzecz: konstruktor klasy ciass2 wywołuje konstruktor Ciassl, aby uzyskać obiekt zawierający metodę gettext, i zwraca ten obiekt jak każdy inny konstruktor. Jednak wiąże się z tym pewien problem, gdyż konstruktor klasy ciassl tworzy obiekt ciassl, a nie Class2. Przy realizowaniu dziedziczenia konieczne jest przepisanie konstruktora ciassl tak, aby można było wywołać gozciass2 i uzyskać obiekt Class2 zamiast Ciassl. Tym zagadnieniem zajmiemy się w następnym punkcie. Dziedziczenie konstruktorów W poprzednim punkcie klasa Class2 dziedziczyła po Ciassl, ale konstruktor ciassl (wywoływany także z Class2) zwraca obiekt ciassl. Aby umożliwić konstruktorowi klasy ciassl tworzenie obiektów Class2 (lub dowolnej innej klasy dziedziczącej po ciassl), zmienimy ten konstruktor i użyjemy w nim dwuparametrowej postaci funkcji bless. Drugi parametr przekazywany bless — o ile istnieje — wskazuje, z jaką klasą ma być związany wskaźnik. W tym przypadku jako drugi parametr przekażemy Class2, zatem funkcja ta utworzy obiekt klasy — Class2. Skąd jednak wiadomo, jaką klasę należy przekazać funkcji bless? Przypomnijmy, że konstruktory są używane jako metody klasy, a pierwszym parametrem przekazywanym metodom klasy jest nazwa klasy, zatem nazwę tę możemy uzyskać jako zwykły parametr. Oto nowy konstruktor klasy ciassl umożliwiający dziedziczenie: package Classl; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } return 1; Użycie takiego konstruktora Classl w poprzednim punkcie spowoduje, że po wywołaniu go z konstruktora Class2 uzyskamy obiekt klasy Class2. Dziedziczenie zmiennych instancji Po klasie bazowej można dziedziczyć nie tylko metody' ale także dane. W Perlu zaleca się zapisywanie danych instancji w asocjacji klas bazowych, na przykład: package Classl; sub new { my $class = shift; my $self = {}; $self->{NAME} = "Christine"; bless $self, $class; return $self; } return 1; Zaleca się stosowanie asocjacji, gdyż jeśli dane zostałyby zapisane w tablicy, w klasach pochodnych problemem mogłoby się stać określenie użytych indeksów, zatem prostsze wydaje się zastosowanie kluczy. Na przykład przy dziedziczeniu z Classl nowe dane uzyskuje się, po prostu dodając w nowej klasie, Class2, dane pod innymi kluczami: package Class2; use Classl; @ISA = qw(Classl); sub new { my $self = Classl->new(); $self->{DATA} = 200; return $self; } return 1; Teraz można odwoływać się do danych bieżącej instancji, która odziedziczyła dane z klasy bazowej: use Class2; my $objectl = Class2->new(); print $objectl->{NAME}, " ma ", $objectl->{DATA}, "\$\n"; Christine ma 200$ Dziedziczenie wielokrotne W Perlu klasa pochodna może dziedziczyć po więcej niż jednej klasie bazowej — wystarczy wszystkie klasy bazowe umieścić w tablicy @isa. Załóżmy na przykład, że mamy dwie klasy bazowe, ClassO i Classl. Klasa ClassO ma metodę printhi: package Class0; sub printhi {print "Hi\n";} return 1; Klasa Classl ma metodę printhello: package Classl; sub printhello {print "Hello\n";} return 1; Możemy utworzyć klasę Class2, która będzie dziedziczyła poClassOiciassl: package Class2; use Class0; use Classl; @ISA = qw(Class0 Classl); sub new { my $self = {}; bless($self); return $self; } return 1; Przy tworzeniu obiektu klasy pochodnej Class2 można używać metod printhi z ClassO i printhello z Classl —na tym właśnie polega dziedziczenie wielokrotne: use Class2; my $objectl = Class2->new(); $objectl->printhi; $objectl->printhello; Hi Hello Przykrywanie metod klasy bazowej Czasami przydatne jest przedefiniowanie metod odziedziczonych po klasach bazowych, co nazywamy przykrywaniem metod. Jeśli na przykład klasa samochód pochodzi od klasy pojazd, to jej metoda gettype, która zwraca wartość „sedan", może przykryć metodę klasy pojazd, zwracającą wartość „pojazd". Aby metodę przykryć, wystarczy japo prostu ponownie zdefiniować. Jeśli trzeba odwołać się do metody pierwotnej, używa się nadklasy — klasy SUPER (w programowaniu obiektowym nadklasa znaczy tyle, co klasa bazowa). Przyjrzyjmy się przykładowi: jako klasy bazowej używamy klasy Classl, która zawiera metodę printem: package Classl; sub printem{print "Hello";} return 1; Następnie wykorzystujemy printem i przykrywamy tę metodę, która występuje w nowej klasie Class2 odziedziczonej po Classl. Do przykrytej metody możemy się odwołać, Stosując zapis SUPER: : printem: package Class2; use Classl; @ISA = qw(Classl); sub new { my $self = {}; bless($self); return $self; } sub printem { $self = shift; $self->SUPER::printem; print " there!"; } return 1; W ten sposób metoda została już przykryta, choć odwołaliśmy się też do metody oryginalnej. W celu obejrzenia wyniku naszych działań możemy za pomocą pr intern wywołać metodę klasy C l a s s 2: use Class2; my $objectl = Class2->new(); $objectl->printem; Hello there! Wiązanie skalarów Perl umożliwia wiązanie zmiennych z klasami, dzięki czemu wartości tych zmiennych są ustawiane przez automatyczne wywoływanie metod związanej klasy. Wiążąc skalar z klasą, można wartości zapisywane w tym skalarze traktować specjalnie. Jako przykład utworzymy klasę Doubler wiążącą skalar tak, że kiedy będziemy odczytywać wartość tego skalara, otrzymamy podwój ona wartość faktyczną. Aby związać skalar z klasą, konieczne jest, aby klasa ta zawierała poniższe metody 'h. (parametr th i s to wskaźnik do wiązanego obiektu): TIESCALAR KLASA, LISTA Wiąże wartość lub wartości podane na LIŚCIE z KLASĄ FETCH THIS Pobiera wartość skalara STORĘ THIS, WARTOŚĆ Zapisuje WARTOŚĆ w skalarze DESTROY THIS Skalar jest usuwany Spójrzmy na kod klasy Doubler: package Doubler; sub TIESCALAR { my $class = shift; $data = shift; return bless \$data, $class; } sub FETCH { my $self = shift; return 2 * $data; } sub STORE { my $self = shift; $data = shift; return 2 * $data; } sub DESTROY { } Teraz za pomocą funkcji Perla, tie, możemy związać skalar z klasą Doubler. Wówczas funkcji tie przekazuje się wiązany skalar i klasę oraz identyfikator bieżącego procesu. Po związaniu skalara z klasą Doubler zapiszemy w nim wartość 5, a po odczytaniu wartości otrzymamy 10: use Doubler; tie $data, 'Doubler’, $$; $data = 5; print "\$data ma wartość $data" $data ma wartość 10 Wiązanie tablic Można wiązać nie tylko skalary (co opisaliśmy w punkcie poprzednim), ale też tablice — należy w tym celu zaimplementować w klasie następujące metody: TIEARRAY KLASA, LISTA Tablica określona LISTĄ jest wiązana z KLASĄ FETCH THIS, INDEKS Pobiera z tablicy wartość o wskazanym indeksie STORE THIS, INDEKS, WARTOŚĆ Zapisuje wartość w tablicy pod wskazanym indeksem DESTROY THIS Tablica jest usuwana FETCHSIZE Określa rozmiar tablicy STORESIZE Ustawia rozmiar tablicy Oto przykład, w którym tworzymy klasę Darray podwajającą wszystkie wartości z tablicy podczas ich odczytywania (parametr this to wskaźnik na wiązany obiekt): package Darray; sub TIEARRAY { my $class = shift; @array = @_; return bless \@array, $class; } sub FETCH { my $self = shift; my $index = shift; return 2 * $array[$index]; } sub FETCHSIZE {return ($#array + 1);} sub STORESIZE {$#array = shift;} sub STORE { my $self = shift; my $index = shift; return 2 * $array[$index]; } sub DESTROY { } return 1; Teraz możemy związać klasę Darray z tablicą— wszystkie wartości odczytywane z tej tablicy są podwojone: use Darray; tie @array, 'Darray', (l, 2, 3); print join (", ", @array) ; 2, 4, 6 Wiązanie asocjacji Aby powiązać asocjację z klasą, trzeba zdefiniować w tej klasie następujące metody (this oznacza wskaźnik na wiązany obiekt): TIEHASH KLASA, LISTA Wiąże pary klucz+wartość z LISTY z KLASĄ FETCH THIS, KLUCZ Pobiera wartość związana z KLUCZEM STORE THIS, KLUCZ, WARTOŚĆ Zapisuje parę KLUCZ+WARTOŚĆ DELETE THIS, KLUCZ Usuwa element o podanym KLUCZU CLEAR THIS Czyści asocjację EXISTS THIS, KLUCZ Sprawdza, czy podany element istnieje FIRSTKEY THIS Zwraca pierwszy klucz NEXTKEY THIS, OSTATNI Zwraca następny klucz (do OSTATNIEGO) DESTROY THIS Wywoływane przy usuwaniu asocjacji Przykład wiązania asocjacji z klasą widzieliśmy w rozdziale 12, kiedy wiązaliśmy asocjację z plikiem DBM. Zagadnienie związane: Zapisywanie pliku bazy danych DBM Czytanie pliku bazy danych DBM Użycie klasy UNIVERSAL W Perlu wszystkie klasy dziedziczą metody po jednej klasie: universal (klasa ta jest niejawnie dodawana na koniec tablicy @isa). W wersji 5.004 Perla klasa ta ma wbudowane pewne metody: isa, can i VERSION. Metoda isa sprawdza tablicę @ISA obiektu lub klasy, jak w poniższym przykładzie, 1 gdzie określamy klasę obiektu $objectl: use Math::Complex; $operandl = Math::Complex->new(l, 2); if ($operandl->isa("Math::Complex")) {print "\$operandl jest obiektem klasy Math::Complex.";} $operandl jest obiektem klasy Math::Complex. Metoda can pozwala sprawdzić, czy przekazany tekst jest nazwą dostępnej w klasie , t • metody, a jeśli tak, to zwraca wskaźnik na tę metodę: $datacall = $objectl->can('getdata'); Metoda version sprawdza, czy klasa lub obiekt ma zmienną globalną $version, która zawiera numer wersji. Oto sposób definiowania wersji: package Classl; $VERSION = 1.01; sub new { my $self = { }; bless $self; return $self; } return l; Metody version obiektu możemy użyć do sprawdzenia jego wersji: use Classl; $objectl = Classl->new; print $objectl->VERSION; 1.01 Rozdział 17. Usuwanie błędów z kodu Perla. Wytyczne dla programistów W skrócie Zapewne piszesz doskonały, czysty kod, który działa od razu i zawsze. Jednak dla niektórych programistów błędy są smutną rzeczywistością, dlatego być może zechcesz jednak przejrzeć ten rozdział, aby móc takim właśnie programistom służyć pomocą. W tym rozdziale zajmiemy się debuggerem Perla. Jeśli Perl zostanie uruchomiony z przełącznikiem -d, program jest uruchamiany w de-buggerze, czyli programie uruchomieniowym. Debugger to środowisko interaktywne umożliwiające wywoływanie specjalnych poleceń, badanie kodu, ustawianie punktów kontrolnych, zmianę wartości zmiennych i inne. Jeśli debugger dołączany do Perla jest niewystarczający, można sięgnąć do jeszcze innych programów tego typu — komercyjnych i niekomercyjnych, a informacji na ten temat należy szukać w archiwum CPAN. Jeśli w systemie zainstalowano edytor GNU Emacs, można go używać w debuggerze, otrzymując w ten sposób w pełni zintegrowane środowisko programistyczne. Debugger Perla jest ukierunkowany na obsługę wierszową, co oznacza, że czasami może wskazać jednocześnie więcej wierszy, niż się mieści w oknie aplikacji. Aby tego problemu uniknąć, należy poprzedzić polecenia debuggera symbolem potoku, l, który powoduje przekazywanie wyników z użyciem stronicowania. Dzięki temu można oglądać wyniki strona po stronie. Przykładowa sesja usuwania błędów Poniżej przedstawimy przykładową sesję pracy z debuggerem. Załóżmy, że mamy skrypt debug.pl: $variablel = 5; $variable2 = 10; $variablel += 5; print "\$variablel = $variablel\n"; print "\$variable2 = $variable2\n"; Aby załadować ten skrypt do debuggera, należy użyć następującego wywołania Perla: %perl -d debug.pl Debugger załaduje nasz skrypt i wyświetli znak zachęty DB<1>, przy czym liczba w nawiasach trójkątnych wskazuje kolejny numer polecenia debuggera: Loading DB routines from perl5db.pl version 1.0401 Emacs support available. Enter h or 'h h’ for help. main::(debug.pl:1) : $variablel = 5; DB<1> Jeśli podamy kreskę, wyświetli się kod programu: DB<1> - 1==> $variablel = 5; 2: $variable2 = 10; 3: $variablel += 5; 4: print "\$variablel = $variablel\n" ; 5: print "\$variable2 = $variable2\n"; Symbol ==> w pierwszym wierszu oznacza wskaźnik debuggera wskazujący bieżący wiersz wykonywanego programu. Aby uruchomić pewną liczbę wierszy kodu i się zatrzymać, ustawia się punkty kontrolne — w naszym przypadku ustawimy taki punkt w wierszu 4. Punkt kontrolny zatrzymuje wykonywanie programu przez debugger w wierszu zawierającym taki punkt. W celu wykonania programu do punktu kontrolnego używa się polecenia c: DB<1> b 4 DB<2> c main::(debug.pl:4): print "\$variablel = $variablel\n" Przyjrzyjmy się jeszcze raz kodowi programu — kursor debuggera przemieścił się do wiersza 4. Warto zauważyć, że znajduje się tam również litera b, która oznacza punkt kontrolny: DB<2> - 1: $variablel = 5; 2: $variable2 = 10; 3: $variablel += 5; 4==>b print "\$variablel = $variablel\n"; 5: print "\$variable2 = $variable2\n"; Kod niekoniecznie trzeba wykonywać aż do kolenjengo punktu kontrolnego, można też za pomocą polecenia s przechodzić do następnego wiersza kodu: DB<2> s $variablel = 10 main:: (debug.pl: 5) : print "\$variable2 = $variable2\n" ; DB<2> Inną techniką, którą warto znać, jest podglądanie wartości zmiennych lub wyrażeń. Przy podglądaniu zmiennej lub wyrażenia debugger pozwala sprawdzać, kiedy taka wartość jest zmieniana. Oto przykład wykorzystania polecenia W do podejrzenia wartości zmiennej $variablel — zauważ, że kiedy debugger napotyka wiersz, w którym wartość badanej zmiennej jest modyfikowana, zatrzymuje się, umożliwiając obejrzenie nowej wartości: DB<1> W $variablel DB<2> c Watchpoint 0: $variablel changed: old value: undef new value: '5' Debugger pozwala jeszcze na wiele więcej, na przykład można zmieniać wartości zmiennych, wyliczać wyrażenia, a nawet uruchamiać kod Perla przed kolejnymi krokami w programie. Perl nie tylko umożliwia użycie debuggera w celu śledzenia kodu, ale zawiera też zestaw sugestii dotyczących tego, jak powinny wyglądać programy zapisane w tym języku. Zagadnienia: Przechwytywanie błędów wykonania Zanim zaczniemy analizować kod i wyszukiwać w nim błędy, warto wspomnieć o możliwości częściowego przechwytywania błędów przy użyciu specjalnych zmiennych: $? (błąd procesu potomnego), $ ! (błąd ostatniego wywołania systemowego), $^E (rozszerzony błąd systemu) oraz $@ (błąd ostatniej instrukcji eval). Jeśli chodzi o przechwytywanie błędów wykonania, Perl nie dysponuje blokami try-catch, jak C++ (w którym podatny na błędy kod umieszcza się w bloku try, zaś błędy io są obsługiwane w bloku catch). Można jednak, korzystając z instrukcji eval, utwo-kt rzyć odpowiednik bloku try. Weźmy pod uwagę przykład, w którym tworzymy procedurę try wykonującą przekazany jej kod. Aby umożliwić try wykonywanie kodu przekazywanego w nawiasach klamrowych, zapiszemy tę procedurę tak, aby mogła jako parametr przyjmować wskaźnik na procedurę anonimową. W celu wykonania otrzymanego kodu z bloku try wywołujemy po prostu anonimową procedurę i zgłaszamy błędy eval, jak w przypadku poniższym, gdzie występuje próba dzielenia przez zero: sub try (&) { my $code = shift; eval {&$code}; if ($@) {print $0;} }; try { $operandl = 1; $operand2 = 0; $result = $operandl / $operand2; }; Illegał division by zero at try.pl line 9. Użycie instrukcji eval pozwala obsługiwać kody błędów — jeśli błędny kod nie byłby uruchamiany w instrukcji eval, powyższy błąd zakończyłby działanie programu. Można też obsługiwać błędy, przechwytując sygnał _ WARN _ . Dzieje się tak w poniższym przykładzie, gdzie w przypadku wystąpienia błędu jest wywoływana procedura obsługi błędu: local $SIG{__WARN__} = sub {print "Błąd!\n"}; Punkt ten w całości został poświęcony błędom wykonania. Jeśli w programie występuje błąd logiczny, to już całkiem inna sytuacja — czas użyć programu uruchomieniowego, czyli debuggera. Uruchamianie debuggera Aby uruchomić w Perlu program w debuggerze, należy uruchomić interpreter z przełącznikiem -d: %perl -d debug.pl Po spełnieniu tego warunku Perl uruchomi wskazany program w debuggerze. Do ustawienia opcji debuggera można użyć też opcji wiersza poleceń -D zestawionych w tabeli 17.1 (opcje debuggera można ustawiać, albo podając literę, jak -Df, albo liczbę, jak -D256). Aby móc używać funkcji usuwania błędów w Perlu, sam Perl musi zostać skompilowany ze specjalną opcją -DDEBUGGING. Opcja ta jest ustawiana automatycznie w przypadku kompilowania Perla z opcją -g. Można używać jednocześnie wielu opcji debugowania, na przykład -Dts. Można też dodać do siebie wartości liczbowe odpowiednich opcji i zestaw opcji podać jako taką sumę (na przykład -Dts równoważne jest -D10). Dostępne polecenia debuggera Aby dowiedzieć się, jakich poleceń można używać w debuggerze, wystarczy wpisać polecenie h: h [polecenie] Tabela 17.1. Opcje usuwania błędów dostępne w wierszu poleceń Wartość Litera Znaczenie 1 P Obsługa leksemów i parsowanie. 2 s Zdejmowanie ze stosu. 4 l Włączenie kontekstowego przetwarzania stosów. 8 t Włączenie uruchamiania śladu. 16 o Włączenie interpretacji metod i przeciążania. 32 c Obsługa konwersji między tekstem a liczbami. 64 P Obsługa preprocesora drukowania. 128 m Włączenie alokacji pamięci. 256 f Włączenie przetwarzania formatów. 512 r Włączenie analizowania i wykonywania wyrażeń regularnych. 1024 x Umożliwia zapisywanie składni w postaci struktur drzewiastych. 2048 u Obsługa kontroli uszkodzonych danych. 4096 L Kontrola obszaru zajętego pamięci. 8192 H Umożliwia zapisywanie asocjacji. 16384 X Włączenie rejestru roboczego. 32768 D Włączenie czyszczenia danych. Jeśli jako parametr zostanie podane polecenie debuggera, prezentowana pomoc będzie dotyczyć tylko tego polecenia. Wynik działania polecenia h jest długi i zawiera opis wszystkich dostępnych poleceń, zaś użycie h h daje skróconą wersję tego zestawienia. Prezentacja kodu Po załadowaniu skryptu do debuggera przydatna mogłaby się okazać możliwość podglądnięcia kodu. Można użyć do tego celu jednego z następujących poleceń: * l — wyświetla następny ekran kodu; * l min+ile— wyświetla ile+1 wierszy, zaczynając od wiersza min; * l min-max — wyświetla wiersze od min do max; * l wiersz — wyświetla wskazany wiersz; * l procedura — wyświetla pierwszy ekran kodu procedury; * - — wyświetla poprzedni ekran; * w [wiersz]—wyświetla wiersze otaczające bieżący wiersz; * . — przemieszcza kursor debuggera do ostatnio wykonywanego wiersza i wyświetla go. Oto przykład wyświetlenia pierwszych trzech wierszy skryptu: DB<1> l 1-3 1==> $variablel = 5; 2: $variable2 += 5; 3: $variablel += 10; Praca „krok po kroku" Debugger umożliwia wykonywanie kodu krok po kroku, do czego służy polecenie s, które realizuje jeden wiersz programu: s [wyrażenie] Jeśli podane zostanie wyrażenie zawierające wywołanie funkcji, jest ona również wykonywana w sposób krokowy. Do powtórzenia ostatniego polecenia s lub n można użyć samego klawisza ENTER. W poniższym przykładzie wykonujemy krok po kroku trzy instrukcje print: DB<1> - 1==> print "Hello\n"; 2: print "from\n"; 3: print "Perl!\n"; DB<1> s Hello main:: (d.pl:2) : print "from\n"; DB<1> s from main: : (d.pl:3): print "Perl!\n"; DB<1> s Perl! Przeskakiwanie wywołań procedur Polecenie n powoduje wykonanie pojedynczego kroku, ale procedury są wykonywane jako tylko jeden krok, czyli debugger „nie wchodzi" do ich wnętrza: n [wyrażenie] Jeśli wyrażenie zawiera funkcję, procedury te zostaną uruchomione, ale nie będą wykonywane krok po kroku. Do powtórzenia ostatniego polecenia s lub n można użyć samego klawisza ENTER. Ustawianie punktów kontrolnych Kiedy debugger napotyka punkt kontrolny, zatrzymuje się i pozwala użytkownikowi przeprowadzić potrzebne kontrole. Punkty kontrolne można ustawiać następująco: * b [wiersz] [warunek] —punkt kontrolny jest ustawiany we wskazanym wierszu. Jeśli wiersz ten nie zostanie podany, punkt kontrolny jest umieszczany w wierszu bieżącym. W sytuacji, gdy zostanie podany warunek, jest on sprawdzany przed każdym uruchomieniem instrukcji, a zatrzymanie następuje tylko wtedy, gdy warunek okaże się prawdziwy; * b procedura [warunek] —punkt kontrolny jest ustawiany w pierwszym wierszu procedury; * b postpone procedura [warunek] —punkt kontrolny jest ustawiany w pierwszym wierszu procedury, ale dopiero po jej kompilacji; * b load plik — ustawia punkt kontrolny na pierwszej instrukcji pliku, którą można wykonać; * b compile procedura — ustawia punkt kontrolny na pierwszej instrukcji procedury, którą można wykonać. Oto przykład ustawienia punktu kontrolnego w czwartym wierszu kodu i uruchomienia kodu — aż do tego wiersza — przy pomocy polecenia c: DB<1> - 1==> print "Hello\n"; 2: print "from\n"; 3: print "Perl!\n"; 4: print "Hello again.\n"; DB<1> b 4 DB<2> c Hello f rom Perl! main::(d.pl:4): print "Hello again.\n"; DB<2> Usuwanie punktów kontrolnych Punkty kontrolne można usuwać za pomocą następujących poleceń: * d [wiersz] — usuwa punkt kontrolny ze wskazanego wiersza. Jeśli nie zostanie podany wiersz, usuwany jest punkt z bieżącego wiersza — o ile się tam znajduje; * D — usuwa wszystkie punkty kontrolne. Wykonywanie kodu do punktu kontrolnego Aby wykonać kod programu aż do punktu kontrolnego, stosuje się polecenie c: c [wiersz|procedura] Instrukcja ta kontynuuje wykonanie programu do następnego punktu kontrolnego lub do wskazanego wiersza lub procedury. Przykłady znajdują się w punkcie „Ustawianie punktów kontrolnych" we wcześniejszej części tego rozdziału. Wyświetlanie wyrażenia Polecenie p pozwala wyświetlić wartość wyrażenia: p wyrażenie W tym przykładzie wyświetlamy wartość zmiennej: DB<1> p $variablel 5 Wyliczanie wartości wyrażenia Debugger pozwala wyliczyć wartość wyrażenia perłowego — wystarczy takie wyrażenie zapisać. Poniżej znajduje się przykład trzykrotnego wyświetlenia napisu. Zauważ występowanie lewych ukośników na końcach wierszy — debugger automatycznie wyświetla „cont:" na początku wierszy, które są kontynuacją wierszy poprzednich: DB<1> for (1..3) { \ cont: print "Hello from Perl!\n"; \ cont: } Hello from Perl! Hello from Perl! Hello from Perl! Zmiana wartości zmiennych Aby zmodyfikować wartość zmiennej, wystarczy przypisać jej nową wartość: DB<1> p $variablel; 5 DB<2> $variablel = 10; DB<3> p $variablel; 10 Ustawianie podglądu Stosując podgląd, można obserwować zmiany wartości zmiennej. Podgląd zakłada się poleceniem W: W [wyrażenie] Oto przykład ustawienia podglądu zmiennej ?var iablel: main::(debug.pl:1): $variablel = 5; DB<1> W $variablel DB<2> c Watchpoint 0: $variablel changed: old value: undef new value : '5' Realizacja zadań debuggera Zadanie debuggera to po prostu kod Perla lub polecenie debuggera uruchamiane przed pojawieniem się znaku zachęty lub po nim. Wśród dostępnych zadań znajdują się następujące: * < [zadanie] — uruchamia kod Perla przed każdym pojawieniem się znaku zachęty; * << zadanie — dołącza kod do kodu uruchamianego przed każdym pojawieniem się znaku zachęty; * > zadanie — uruchamia kod Perla po każdym pojawieniu się znaku zachęty; * >> zadanie — dołącza kod do kodu uruchamianego przed każdym pojawieniem się znaku zachęty; * { [zadanie] —przygotowuje zadanie debuggera, uruchamiane przed pojawieniem się znaku zachęty; * {{ zadanie — dodaje zadanie do zadań wykonywanych przed pojawieniem się znaku zachęty; * a [wiersz] zadanie — ustawia zadanie, które ma być wykonane przed uruchomieniem danego wiersza; * A — usuwa wszystkie zadania. Oto przykład, w którym wyświetlamy wartość zmiennej $variablel przed każdym pojawieniem się znaku zachęty: DB<1> -f 1==> $variablel = 5; [ 2: $variablel += 5; 3: $variablel += 5; DB<1> < print "\$variablel = $variablel\n"; DB<2> s main::(debug.pl:2): $variablel += 5; $variablel = 5 DB<2> s main: : (debug.pl:3) : $variablel += 5; $variablel = 10 Kończenie pracy z debuggerem Pracę z debuggerem kończymy, wydając polecenie q. Wytyczne dla programistów Perla Programiści używający Perla otrzymują bardzo długą listę zaleceń, których część za moment wymienimy. Wiele z nich tak naprawdę dotyczy stylu programowania, więc w trakcie przeglądania poniższej listy możesz się zgadzać z niektórymi punktami, ale być może z pewnymi będziesz polemizować. * Blok składający się z jednej instrukcji można zapisywać w pojedynczym wierszu (wraz z nawiasami klamrowymi). * Odpowiadające sobie fragmenty kodu wyrównuj w pionie. * Zawsze sprawdzaj kod powrotu wywołania systemowego i wykonania instrukcji w odwróconych apostrofach. * Identyfikatory dobieraj tak, aby po roku przerwy nadal coś znaczyły. * Staraj się używać zawsze use strict. * Nie staraj się za wszelką cenę opuszczać pętli na jej początku lub końcu, gdyż Perl daje wygodne metody przerwania j ej pośrodku. * Nie używaj średnika w blokach jednowierszowych. * Nie wstawiaj spacji przed średnikiem ani między nazwą funkcji a nawiasami. * Jeśli poprawi to czytelność kodu, między jego poszczególnymi fragmentami wstawiaj puste wiersze. * Długie wiersze dziel po operatorze (nie dotyczy to operatorów and i or). * To, że nawiasy można pominąć, nie oznacza, że trzeba tak postępować. Jeśli mogą powstać jakieś wątpliwości co do interpretacji wyrażeń, zostaw nawiasy. * Staraj się, aby Twój kod mógł być wielokrotnie wykorzystywany. Być może warto zamknąć pewne jego fragmenty w moduły lub klasy. * W przypadku wielowierszowych bloków — otwierający nawias klamrowy umieszczaj w tym samym wierszu, w którym znajduje się słowo kluczowe dotyczące bloku. * Używaj początkowego podkreślenia do wskazania, że zmienna lub funkcja jest prywatna dla pakietu. * Po wszystkich przecinkach, w złożonych indeksach ujętych w nawiasy klamrowe, dookoła większości operatorów i przed nawiasami klamrowymi wstawiaj spacje. * Zamiast wielu instrukcji print używaj dokumentów typu here. * Nazwy metod i funkcji zapisuj małymi literami. * Używaj opcji -w (zawsze). * Dopilnuj, aby wcięcia w kodzie miały cztery znaki. * Stosując konstrukcje, które mogą w niektórych systemach nie działać, używaj instrukcji eval i sprawdzaj, czy kod zadziałał. Część IV Programowanie CGI Rozdział 18. Programowanie CGI W skrócie W tym rozdziale zaczniemy studia nad wspólnym interfejsem bramy (Common Gateway Interface, CGI). Temat ten wśród programistów Perla jest bardzo popularny (zresztą dla niektórych programistów jest to jedyna racja bytu Perla). Programowanie CGI opiera się na skryptach CGI, zaś z punktu widzenia Perla taki skrypt to po prostu program perłowy, który ma zwykle rozszerzenie .cgi. Skrypty umieszcza się na komputerze dostawcy usług internetowych (ISP), aby ożywić strony przyciskami, listami wyboru, menu i innymi elementami graficznymi. Dzięki CGI użytkownicy mogą sięgać do baz danych, uruchamiać programy, grać w gry, a nawet robić przez Internet zakupy. To właśnie Perl jest odpowiedzialny za interakcyjne zachowania dziesiątek tysięcy witryn. W tym rozdziale poznamy podstawowe techniki programowania skryptów CGI, a przy tym przygotujemy dwa skrypty. Pierwszy skrypt cgil.cgi utworzy stronę pełną elementów graficznych HTML (przycisków, list wyboru, przycisków, menu i innych). Kiedy użytkownik kliknie przycisk Wyślij, to drugi skrypt cgi2.cgi odczyta dane i prześle je z powrotem. W tym i w następnych trzech rozdziałach zakładamy, że masz dostawcę usług internetowych oraz miejsce u tego dostawcy, gdzie możesz umieszczać swoje strony (zwykle jest to kwestia użycia najprostszego programu FTP lub strony dostawcy, która pozwala załadować stronę z komputera lokalnego). Konieczna jest także możliwość uruchamiania skryptów CGI na komputerze ISP, gdyż niektórzy dostawcy Internetu tego nie zapewniają, chcąc w ten sposób zwiększyć poziom bezpieczeństwa. Jeśli możesz uruchamiać skrypty CGI, nie zapomnij o ustawieniu odpowiednich uprawnień do tych plików (tak, aby nie narazić przy tym systemu na włamania). Na przykład w przypadku komputerów pracujących pod kontrolą systemu operacyjnego Unix — do ustawienia uprawnień do pliku stosuje się polecenie chmod (przykładowe użycie to chmod 644 script. cgi). W przypadku plików wykonywalnych konieczne jest też przyznanie uprawnień do wykonania: chmod +x script.cgi. Dokładniejszych informacji o umieszczaniu stron na serwerze dostawcy Interneru trzeba szukać już u niego w dziale pomocy technicznej. Użycie CGI.pm Jak zatem tworzy się skrypty CGI? Teoretycznie jest to bardzo proste. Program CGI zawiera zwykły kod Perla, który jest uruchamiany, gdy przeglądarka wywoła adres URL pliku ze skryptem. Wszystko, co zostanie zapisane przez skrypt do standardowego wyjścia, jest przekazywane przeglądarce. Jeśli skrypt uruchomi jakąś instrukcję, na przykład print "Hello! ", pokazywany tekst zostanie odesłany do przeglądarki i w jej oknie pokaże się tekst „Hello!". Jednak jest to działanie elementarne, jak zatem odczytać dane przypisane do elementów graficznych formularza HTML? Jak taki formularz wygenerować? To i wiele innych rzeczy można wykonać za pomocą pakietu CGI.pm rozpowszechnianego wraz z Perlem (w następnym rozdziale zastosujemy inny popularny pakiet, cgi-lib.pl). Użycie CGI.pm to typowy sposób kodowania CGI w Perlu, więc temat ten omówimy dość dokładnie. Pakiet CGI.pm jest dostarczany wraz z Perlem, zatem jeśli w systemie znajduje się już Perl, odpada trud instalowania tego pakietu. Od wersji 5. Perla pakiet ten został zapisany jako obiektowy, choć istnieje nadal prostszy interfejs proceduralny (składający się z funkcji). W tym rozdziale będziemy używać interfejsu obiektowego. Za pomocą pakietu CGI.pl utworzymy obiekt CGI, a następnie będziemy wywoływać poszczególne jego metody. Każdy ważniejszy znacznik HTML ma odpowiadającą mu metodę — wywoływanie tych metod powoduje generowanie odpowiednich znaczników na podstawie przekazywanych parametrów. Wszystkie metody CGI.pm używają parametrów nazwanych (oprócz metod mających tylko jeden parametr), co oznacza, że oprócz wartości przekazuje się także nazwę atrybutu HTML, którego dana wartość dotyczy. Oto przykład, w którym za pomocą obiektu CGI tworzymy stronę sieciową. Zwróć uwagę na przekazywanie metodzie textarea, która tworzy obszar tekstowy, nazwanych parametrów zawierających nazwę, wartość domyślną i rozmiar: use CGI; $co = new CGI; print $co->header, $co->start_html(-title=>'Przykład CGI'), $co->center($co->h1('Witaj w krainie CGI!’)), $co->textarea( -name=>'textarea ' , -default=>'Brak zdania’, -rows=>10, -columns=>60 ), $co->end_html; Pakiet CGI.pm zawiera także prosty funkcyjny interfejs programistyczny, który nie wymaga stosowania technik obiektowych. Z tym interfejsem zapoznamy się pod koniec niniejszego rozdziału. Tworzenie i używanie elementów graficznych HTML Programowania najlepiej uczyć się na przykładach, więc —jak to już zapowiedziano — w tym rozdziale przygotujemy dwa skrypty CGI: pierwszy utworzy stronę sieciową wypełnioną elementami graficznymi HTML, drugi odczyta dane wprowadzone na tej stronie. Oba skrypty zawierają niewiele więcej ponad jedną długą instrukcję print, która tworzy stronę. Pierwszy skrypt, cgil.cgi, pokazano na wydruku 18.1. Kiedy użytkownik otworzy ten skrypt w swojej przeglądarce (wskazując jego adres URL, na przykład http://www.serwer. com.pl/user/cgi/cgil.cgf), skrypt ten zwróci stronę zawierającą ankietę w postaci formularza HTML. Wygląd tej ankiety w przeglądarce Netscape Navigator pokazano na poniższych trzech rysunkach. Jak to przedstawiono na rysunku 18.1, strona najpierw wita użytkowników i informuje, że jeśli nie chcą wypełniać ankiety, mogą przejść do witryny CPAN. Po przesunięciu się nieco w dół strony pojawia się obraz, jak na rysunku 18.2. Tym razem użytkownik jest proszony o podanie w polu tekstowym imienia oraz wpisanie swojego zdania w obszarze tekstowym HTML (obszar tekstowy to rodzaj dwuwymiarowego pola tekstowego). W miarę przesuwania się dalej pokazują się dalsze elementy graficzne (rysunek 18.3): pola opcji, przyciski radio, listy wyboru, menu i przyciski Wyślij oraz Zapytaj. Sposób tworzenia wszystkich tych elementów graficznych w skrypcie CGI zostanie pokazany dalej w tym rozdziale. Kiedy użytkownik kliknie przycisk Wyślij na dole ankiety, przeglądarka zbierze z formularza wprowadzone dane i wyśle je skryptowi cgi2.cgi. Ten skrypt pokazano na wydruku 18.2, zaś jego wyniki zaprezentowano na rysunku 18.4 —jest to strona, na której zestawiono informacje zebrane z ankiety. Skąd strona z ankietą ma wiedzieć, gdzie wysłać zebrane informacje? Wszystkie elementy graficzne z tej strony należą do jednego formularza HTML, który jest formularzem niewidocznym na ekranie, a zarazem jest konstrukcją języka HTML, zawierającą zestaw elementów graficznych. Atrybut action formularza zawiera adres URL skryptu cgi2.cgi. Kiedy użytkownik kliknie przycisk Wyślij, przeglądarka wysyła dane spod elementów graficznych formularza pod wskazany adres URL. Skrypt cgi2.cgi odczytuje i wyświetla otrzymane dane. Zanim przejdziemy do opisu poszczególnych zagadnień, musimy zwrócić uwagę na jeszcze jedną ważną kwestię. Strona z ankietą wcale nie musi być generowana przez skrypt CGI, można zastosować zwykłą, statyczną stronę HTML, która wywołuje cgi2.cgi po kliknięciu przycisku Wyślij. Skryptu cgil.cgi, który tworzy tę stronę dynamicznie, używamy po to, aby pokazać obie części procesu wykonane za pośrednictwem CGI: generowanie elementów graficznych HTML oraz odczytywanie danych. Na wydruku 18.3 pokazano stronę wygenerowaną przez cgil.cgi. Wydruk 18.1. cgi1.cgi !/usr/local/bin/perl use CGI; $co = new CGI; $labels( '!'} = $labels{'2'} = $łabels{'3'} = $labelsf'4 ' } = $labels{'5'} = $labels{'6'} = $labels{'7') = 'Niedziela'; 'Poniedziałek' 'Wtorek1; 'Środa'; 'Czwartek1; 'Piątek1; 1 Sobota'; nty :em taw cgi. tów •zez '•cgi nie, ge- print $co->header, $co->start_html( -title=>'Przykład CGI', -author=>'Steve', -meta=>{'keywords'=>'Perl CGI'), -BGCOLOR=>'white', -LINK=>'red' $co->center($co->hl('To jest ankieta!')), $co->h2('Prosimy o wypełnienie naszej ankiety... "Co daje wypełnienie naszej ankiety:", $co->p, $co->ul ( $co->li('Sława1), $co->li('Pieniądze'), $co->li('Zabawa'), "Jeśli nie masz ochoty naszej ankiety wypełniać, "być może zainteresuje Cię witryna ", $co->a((href=>"http://www.cpan.org/"},"CPAN"), " $co->hr, Sco->startform( -method=>'POST' , -action=>"http://www.serwer.com/user/cgi/cgi2.cgi"), "Proszę podać swoje imię: ", $co->textfield('text'), $co->p, "Proszę wpisać tu swoją opinię: ", $co->p, $co->textarea( -name=>'textarea', -default=>'Brak zdania1, -rows=>10, -columns=>60 ), $co->p, "Proszę wskazać używane produkty: ", $co->p, $co->checkbox_group( -name=>'checkboxes', -values=>['Szampon1, 'Pasta do zębów1, 'Chleb1, dociski balistyczne1], -defaults=>['Chleb1, 'Pociski balistyczne'] ) , $co->p, "Proszę określić szacunkowy przychód: ", $co->p, $co->scrolling_list( 'list', ['Najwyższy1, 'Wysoki', 'Średni1, 'Niski1], 'Wysoki', ), $co->p, "Proszę podać dzień tygodnia: ", $co->p, $co->radio_group( -name=>'radios', -values=>['l','2','3','4','5','6','7'], -default=>'l', -labels=>\%labels ), $co->p, "Dziękujemy za wypełnienie ankiety. Proszę określić, ile poczty chciałbyś otrzymywać: ", $co->popup_menu( -name=>'popupmenu', -values=>['Mnóstwo', 'Dużo1, 'Niezbyt wiele', 'Wcale'] ), $co->p, $co->hidden(-name=>'hiddendata' , -default=>'Różany pączek'), $co->center( $co->submit, $co->reset, ) , $co->hr, $co->endform, $co->end html; Wydruk 18.2. cgi2.cgi #!/usr/local/bin/perl use CGI; $co = new CGI; print $co->header, $co->start_html( -title=>'Przykład CGI1, -author=>'Steve', -meta=>('keywords'=>'Perl CGI'}, -BGCOLOR=>'white ' , -LINK=>'red' ), $co->center($co->hl('Dziękujemy za wypełnienie ankiety.1)), $co->h3(10to Twoje odpowiedzi...'), i Sco->hr; if ($co->param() ) ( print "Na imię masz: ", $co->em($co->param('text')), • ".", $co->p, i "Twoje zdanie: ", Sco->em($co->param('textarea')), ".", $co->p. "Używasz następujących produktów: ", $co->em (join(", ", i $co->param('checkboxes'))), ".", $co->p, t "Poziom przychodów: ", $co->em($co->param('list' )), ".", $co->p, "Dziś jest ", $co->em($co->param('radios')), ". dzień i tygodnia", $co->p, l "Mile widziana ilość poczty: ", em($co->param( 'popupmenu1 ) ) , ".", $co->p, "Dane ukryte to ", $co->em(join(", ", $co->param('hiddendata'))), "."; ) j print $co->hr; i print $co->end_html; Wydruk 18.3. Wygenerowana strona HTML Przykład CGK/TITLE> .', ' To jest ankieta ! '

Prosimy o wypełnienie naszej ankiety...

Co daje wypełnienie naszej ankiety: SławaPieniądzeZabawa Jeśli nie masz ochoty naszej ankiety wypełniać, •, być może zainteresuje Cię witryna CPAN. Proszę podać swoje imię: , Proszę wpisać tu swoją opinię: Brak zdania Proszę wskazać używane produkty:

l Szampon ' Pasta do zębów Chleb Pociski balistyczne

Proszę określić szacunkowy przychód:

Proszę podać dzień tygodnia:

Sobota

Dziękujemy za wypełnienie ankiety. Proszę określić, ile poczty chciałbyś otrzymywać: •CINPUT TYPE="hidden" NAME=".cgifields" VALUE= "radios"> "checkboxes"> Zagadnienia: Użycie PerlScriptu Ten rozdział zaczniemy w sposób, jakiego z pewnością się nie spodziewasz: od PerlScriptu, języka interpretowanego, który działa w niektórych przeglądarkach, takich jak na przykład Microsoft Internet Explorer. Wprawdzie sam PerlScript do tematu tej książki nie należy, ale warto wiedzieć, że coś takiego istnieje, gdyż zamiast tworzyć w pełni złożone (wynikające ze standardu CGI) programy CGI, można wstawić do strony kod PerlScriptu. Oto przykład użycia PerlScriptu do wyświetlenia na stronie „Hello!": Przykład PerlScriptu

Przykład PerlScriptu

W Internet Explorerze otworzy się taka strona, jaką pokazano na rysunku 18.5. Początek dokumentu HTML w CGI Aby zacząć dokument HTML, najpierw tworzy się obiekt CGI. Potem metodą header tego obiektu tworzy się nagłówek HTML (tutaj przygotujemy bardzo prosty nagłówek, ale nagłówki te mogą być dowolnie skomplikowane, na przykład wzbogacone o cookies i inne atrybuty). Dokument zaczyna się metodą start_html, która tworzy sekcję strony i umożliwia wskazanie atrybutów sekcji , na przykład kolorów tła i łączy. W poniższym przykładzie pokazujemy, w jaki sposób zaczynamy stronę z ankietą w cgil.cgi (do umieszczenia wyników header i start_html na stronie używa się funkcji print): #!/usr/local/bin/perl $co = new CGI; print $co->header, $co->start_html( -title=>'Przykład CGI’, -author=>'Steve', -meta=>{'keywords'=>'Perl CGI'}, -BGCOLOR=>'white', -LINK=>'red' ) Tworzenie nagłówków HTML Po przygotowaniu nagłówka strony można wykorzystać h l, h2 i h3 do tworzenia nagłówków HTML, odpowiednio

,

,

i tak dalej. Oto sposób utworzenia dwóch nagłówków:

i

(na górze strony z ankietą), w których umieszczamy powitanie odbiorcy: !/usr/local/bin/perl $co = new CGI; print ... $co->h1('To jest ankieta!'), $co->h2('Prosimy o wypełnienie naszej ankiety...'), Wyniki pokazano na rysunku 18.1 we wcześniejszej części tego rozdziału. Wyśrodkowanie elementów HTML Tekst możemy wyśrodkować za pomocą znacznika
, używając metody cen-ter obiektu CGI. Oto przykład wyśrodkowania znacznika

utworzonego w poprzednim punkcie: #!/usr/local/bin/perl $co = new CGI; print ... $co->center($co->h1('To jest ankieta!')), $co->h2('Prosimy o wypełnienie naszej ankiety ...'), Wyniki pokazano na rysunku 18.1 we wcześniejszej części tego rozdziału. Tworzenie list HTML Metody ul i li obiektu CGI pozwalają generować znaczniki
    i
  • , które pokazują listy wyliczane. Oto sposób pokazania takiej listy użytkownikowi na naszej stronie z ankietą— wyjaśniamy mu, jakie korzyści płyną z wypełnienia tej ankiety: #!/usr/local/bin/perl $co = new CGI; print ... "Co daje wypełnienie naszej ankiety:", $co->p, $co->ul( $co->li('Sława'), $co->li('Pieniądze'), $co->li('Zabawa'), ) Wyniki działania tego kodu pokazano na rysunku 18.1 we wcześniejszej części tego rozdziału. Tworzenie hiperiączy Hiperłącze można tworzyć za pomocą metody a obiektu CGI, umożliwiając użytkownikowi przejście pod inny adres, jeśli tylko nie jest zainteresowany wypełnieniem naszej ankiety: #!/usr/local/bin/perl $co = new CGI; print ... "Jeśli nie masz ochoty naszej ankiety wypełniać, ", "być może zainteresuje Cię witryna ", $co->a({href=>"http://www.cpan.org/"},"CPAN"), ".", Wyniki działania tego kodu pokazano na rysunku 18.1 we wcześniejszej części tego rozdziału. Tworzenie poziomej linii Poziomą linię w HTML realizuje znacznik
    generowany przez metodę hr: #!/usr/local/bin/perl $co = new CGI; print ... $co->hr Linia pojawia się na dole rysunku 18. l pokazanego we wcześniejszej części tego rozdziału. Tworzenie formularza HTML Aby użyć na stronie HTML elementów graficznych, trzeba zamknąć je w formularzu HTML. Do utworzenia formularza wykorzystuje się metodę startf orm obiektu CGI, a kiedy użytkownik kliknie przycisk SUBMIT (który zaraz też utworzymy), dane spod elementów graficznych tego formularza zostaną wysłane do skryptu cgi2.cgi generującego podsumowanie. Docelowy skrypt cgi2.cgi wskazujemy, umieszczając jego adres URL w atrybucie action formularza: #!/usr/local/bin/perl $co = new CGI; print ... $co->startform ( -method=>'POST', -action=>"http://www.serwer.com/user/cgi/cgi2.cgi") #$co->startform() Wszystkie elementy graficzne opisywane dalej w kolejnych punktach, aż do punktu „Zakończenie formularza HTML", są zamknięte w formularzu, gdyż wywołanie startf orm wstawia do dokumentu znacznik
    . Jeśli metoda startform zostanie wywołana bez żadnych parametrów, przycisk Submit wyśle dane z formularza do tego samego formularza. Przykład ten zostanie omówiony w następnym rozdziale. Używanie pól tekstowych Aby utworzyć pole tekstowe, w którym użytkownik może wpisywać dane, używa się metody textfield obiektu CGI. Oto sposób utworzenia pola tekstowego i nazwania go imieniem użytkownika: #!/usr/local/bin/perl $co = new CGI; print ... "Proszę podać swoje imię: ", $co->textfield('text') Utworzone w taki sposób pole tekstowe pokazano na rysunku 18.2 we wcześniejszej części tego rozdziału. Odczytywanie danych spod elementów graficznych HTML Mamy już elementy graficzne, do których odnoszą się dane, na przykład omówione przed chwilą pole tekstowe, zatem teraz trzeba zastanowić się, jak odczytać te dane, kiedy użytkownik kliknie przycisk Submit. Kiedy użytkownik kliknie przycisk Submit ankiety, przeglądarka prześle dane z formularza do skryptu cgi2.cgi. Skrypt ten za pomocą metody param obiektu CGI czyta dane z pola tekstowego. Aby użyć param, trzeba przekazać nazwę pola — w tym przypadku text — i można wyświetlić dane uzyskane w wyniku działania param: ł!/usr/local/bin/perl $co = new CGI; print "Na imię masz: „, $co->em($co->param('text')), „.”; Metoda em tworzy znacznik , który jest interpretowany przez większość przeglądarek jako nakaz użycia kursywy. Wyniki można obejrzeć na rysunku 18.4 zamieszczonym wcześniej w tym rozdziale. Używanie obszarów tekstowych Obszary tekstowe — w przeciwieństwie do pól tekstowych — mogą zawierać kilka wierszy tekstu. Oto sposób utworzenia w skrypcie cgil.cgi obszaru tekstowego o wielkości 10 wierszy i 60 kolumn, zawierającego tekst domyślny — obszar ten nazwiemy textarea: #!/usr/local/bin/perl $co = new CGI; print ... "Proszę wpisać tu swoją opinię: „, $co->p, $co->textarea( -name=>'textarea', -defaułt=>'Brak zdania', -rows=>10, -columns=>60 ) Wyniki można obejrzeć na rysunku 18.2 zaprezentowanym wcześniej w tym rozdziale. Poniżej przedstawiamy sposób użycia metody param obiektu CGI do odczytania tekstu z obszaru tekstowego w skrypcie cgi2.cgi (wynik tego odczytu pokazano na rysunku 18.4): print "Twoje zdanie: ", $co->em($co->param('textarea')), "."; Używanie pól opcji Pól opcji używa się w grupach (wyniki z takich pogrupowanych pól opcji są razem i zwracane w postaci jednej listy). Do tworzenia grupy opcji używa się metody check-box_group; w skrypcie cgil.cgi tworzymy grupę opcji umożliwiającą użytkownikowi wskazanie używanych produktów. Grupie nadajemy nazwę, przekazujemy etykiety i poszczególnych opcji i wskazujemy, które pola mają być zaznaczone domyślnie po pojawieniu się strony: #!/usr/local/bin/perl $co = new CGI; print ... "Proszę wskazać używane produkty: ", $co->p, $co->checkbox_group( -name=>'checkboxes', -values=>['Szampon', 'Pasta do zębów', 'Chleb', , 'Pociski balistyczne1], -defaults=>['Chleb', 'Pociski balistyczne'] ) Wyniki przedstawiono na rysunku 18.3 znajdującym się wcześniej w tym rozdziale. Dane z tych pól opcji odczytujemy i wyświetlamy w skrypcie cgi2.cgi, którego wyniki działania pokazano na rysunku 18.4. Zwróć uwagę, że metoda param zwraca w tym przypadku listę wartości, którą możemy połączyć funkcją j oin w jeden tekst: print "Używasz następujących produktów: ", $co->em(join(", ", $co->param('checkboxes'))), "."; Używanie list wyboru Lista wyboru służy do wyświetlenia użytkownikowi zestawu wartości, przy czym listę ; można przewijać, jeśli wszystkie jej pozycje nie mieszczą się na raz. Do tworzenia list 1 wyboru służy metoda scrolling_list. W skrypcie cgil.cgi wygenerowaliśmy listę, która umożliwia użytkownikowi określenie swoich dochodów, a jako wybraną wartość określiliśmy przychód wysoki: #!/usr/local/bin/perl $co = new CGI; print ... "Proszę określić szacunkowy przychód: ", $co->p, $co->scrolling_list( 'list', ['Najwyższy', 'Wysoki1, 'Średni', 'Niski'], 'Wysoki', ): Wyniki pokazano na rysunku 18.3 we wcześniejszej części tego rozdziału. Zwróćmy uwagę na to, jak odczytujemy w skrypcie cgi2.cgi wybrane pozycje: print "Poziom przychodów: ", $co->em($co->param('list')), "."; Używanie przycisków radio Użycie przycisków radio umożliwia użytkownikowi wybranie jednej z wielu wzajemnie się wykluczających opcji. Na przykład w cgil.cgi używamy siedmiu przycisków radio, które umożliwiają użytkownikowi wybranie dnia tygodnia. Grupę przycisków nazywamy radios, przy czym poszczególnym przyciskom przypisujemy wartości od ' l' do ' 7 ', a w asocjacji %labels umieszczamy etykiety poszczególnych przycisków: #!/usr/local/bin/perl $co = new CGI; $labels{‘1’} = 'Niedziela'; $labels{‘2’} = 'Poniedziałek $labels{‘3’} = 'Wtorek'; $labels{‘4’} = ' Środa ' ; $labels{‘5’} = 'Czwartek' ; $labels{‘6’} = 'Piątek'; $labels{‘7’} = ' Sobota ' ; print ... "Proszę podać dzień tygodnia: ", $co->p, $co->radio_group( -name=>'radios', -values=>['l','2','3','4','5','6','7'], -de?ault=>'l', -labels=>\%labels ) Wyniki działania tego kodu pokazano na rysunku 18.3. Poniżej przedstawiamy sposób odczytania wybranej wartości w skrypcie cgi2.cgi (rezultat tego odczytania pokazano na rysunku 18.4): print "Dziś jest ", $co->em($co->param('radios')), ". dzień tygodnia"; Używanie menu Rozwijane menu w HTML, znane użytkownikom Windows jako lista rozwijana z polem edycyjnym, daje użytkownikowi możliwość wyboru jednej z wielu opcji — po kliknięciu umieszczonego z boku przycisku. W poniższym przykładzie zobaczysz, jak przy pomocy metody popup_menu wygenerowano menu, które umożliwia użytkownikowi wskazanie ilości chętnie widzianej poczty: #!/usr/local/bin/perl $co = new CGI; print ... "Dziękujemy za wypełnienie ankiety. Proszę określić, ile poczty chciałbyś otrzymywać: ", $co->popup_menu ( -name=> ' popupmenu ' , -values=>[ 'Mnóstwo’, 'Dużo’, 'Niezbyt wiele', 'Wcale'] ) Wyniki działania tego kodu są widoczne na rysunku 18.3. Oto sposób odczytania i wyświetlenia wartości wybranej przez użytkownika w skrypcie cgi2.cgi: print "Mile widziana ilość poczty: ", $co->em($co->param('popupmenu’)), Użycie pól ukrytych Dane-na stronie sieciowej można przechowywać w polach ukrytych, czyli takie dane są niewidoczne dla użytkownika (jest to wygodne, jeśli potrzebne jest zapisanie danych związanych z samą stroną). W skrypcie cgil.cgi generujemy ankietę, wstawiając do niej także dane ukryte: #!/usr/local/bin/perl $co = new CGI; print ... $co->hidden(-name=>'hiddendata', -default=>'Różany pączek'); W skrypcie cgi2.cgi dane te odczytujemy i wyświetlamy (rysunek 18.4): print "Dane ukryte to ", $co->em(join(", ", $co->param('hiddendata'))), "."; Tworzenie przycisków SUBMIT i RESET Aby przesłać dane z formularza, użytkownik musi kliknąć przycisk Submit. Do jego tworzenia służy metoda submit obiektu CGI. Można też utworzyć przycisk Reset, który czyści cały formularz, a stosuje się do tego metodę reset. Oto przykład dodania obu tych przycisków w skrypcie cgil.cgi: #!/usr/local/bin/perl $co = new CGI; print ... $co->center ( $co->submit, $co->reset, ) Wyniki widać na rysunku 18.3. Kiedy użytkownik kliknie przycisk Submit, dane z formularza są przesyłane do skryptu cgil.cgi. Zakończenie formularza HTML Wszystkie utworzone dotąd elementy graficzne są częścią jednego formularza ankiety tworzonej przez skrypt cgil.cgi. Formularz ten został utworzony metodą startf orm, zaś do jego zakończenia używa się metody endf orm: #!/usr/local/bin/perl $co = new CGI; print ... $co->endform Zakończenie dokumentu HTML Aby zakończyć dokument HTML, używa się metody'end_html obiektu CGI. Metoda ta dodaje znaczniki . Oto przykład zakończenia naszej ankiety w skrypcie cgil.cgi: #!/usr/local/bin/perl $co = new CGI; print ... $co->end_html; W ten sposób cgil.cgi jest gotowy. Kiedy przeglądarka uruchomi ten skrypt, pokaże się strona jak na rysunkach 18.1, 18.2 i 18.3. Jeśli po wprowadzeniu danych użytkownik kliknie przycisk Submit, dane z tej strony zostaną wysłane do skryptu cgi2.cgi, który wyświetla zestawienie tych danych, jak na rysunku 18.4. Programowanie CGI przy pomocy funkcji Jak dotąd, używając pakietu CGI, stosowaliśmy metody obiektowe, ale pakiet ten zawiera także interfejs funkcyjny (choć nie wszystkie metody obiektowe są w nim obsługiwane). Oto przykład użycia interfejsu funkcyjnego do wygenerowania pola tekstowego, w którym użytkownik może się przedstawić. Kiedy to zrobi i kliknie przycisk Submit, dane z pola są odsyłane do tego samego skryptu CGI, który przy pomocy metody param wyświetla podane imię na dole strony wynikowej: #!usr/local/bin/perl use CGI qw/:standard/; print header, start_html('Przykład użycia funkcji CGI'), h1('Przykład użycia funkcji CGI'), start_form, "Proszę podać swoje imię: ", textfield('text'), p, submit, reset, end_form, hr; if (param()) { print "Podane imię to ", em(param('text')), hr; } print end_html; Wyniki działania skryptu pokazano na rysunku 18.6. Rozdzjał 19. Programowanie CGI przy użyciu cgi-lib.pl W skrócie W poprzednim rozdziale omawialiśmy tworzenie programów CGI przy pomocy pakietu Perla CGI.pm. Programiści Perla, którzy chcą używać CGI, mają do dyspozycji inne narzędzie: cgi-lib.pl, a jako że pakiet ten jest bardzo popularny, zostanie omówiony w niniejszym rozdziale. Właścicielem praw autorskich do cgi-lib.pl jest Steven E. Brenner, a plik ten znajdziesz na jego witrynie pod adresem http://cgi-lib.stanford.edu/cgi-lib. Wspomnianego pakietu wolno używać i można go nawet modyfikować, natomiast konieczne jest zachowanie informacji o prawach autorskich na początku pliku. Niepotrzebne są żadne specjalne techniki instalacji — wystarczy ściągnąć ten plik do siebie i zapisać w katalogu, w którym są skrypty CGI, zaś na początku skryptów trzeba dodawać instrukcję reąuire: require 'cgi-lib.pl’; W tym rozdziale utworzymy dwa skrypty CGI analogiczne do skryptów z poprzedniego rozdziału, ale tym razem zamiast CGI.pm użyjemy pakietu cgi-lib.pl. W poprzednim rozdziale skrypty nazwaliśmy cgil.cgi \ cgi2.cgi, zatem teraz, żeby pliki się nie myliły, nadamy im nazwy libl.cgi i Iib2.cgi. Skrypty te dadzą identyczne wyniki, jak skrypty z poprzedniego rozdziału. Pierwszy skrypt to libl.cgi, którego kod podano na wydruku 19.1. Kiedy użytkownik uruchomi ten skrypt w przeglądarce (podając w okienku adresowym jego adres URL, na przykład http://www.serwer.com/user/cgi/libl.cgi), libl.cgi zwróci stronę HTML, która zawiera elementy graficzne i dodatkowy tekst, składające się na ankietę. Ankietę tę pokazano na rysunkach 19.1, 19.2 i 19.3. Najpierw witamy użytkownika i informujemy, że jeśli nie chce wypełniać ankiety, może udać się na witrynę CPAN, korzystając z przygotowanego łącza (rysunek 19.1). W dalszej części ankiety znajdują się elementy graficzne (rysunek 19.3): przyciski radio, pola opcji, listy wyboru, menu i przyciski Submit oraz Reset. Wszystkie te elementy graficzne służą do zbierania dalszych informacji od użytkownika. Kiedy użytkownik kliknie przycisk Submit na dole ankiety, przeglądarka zbiera wprowadzone dane i wysyła je do kolejnego skryptu CGI, Iib2.cgi. Skrypt ten pokazano na wydruku 19.2, zaś wyniki jego działa na rysunku 19.4. Wspomniany skrypt podsumowuje dane wprowadzone przez użytkownika i zwraca je w postaci strony HTML. Tak w skrócie wyglądają skrypty, którymi zajmiemy się do końca tego rozdziału. Teraz krótko wyjaśnimy, co składa się na cgi-lib.pl. Użycie cgi-lib.pl Aby użyć cgi-lib.pl, zwykle stosuje się w skryptach instrukcję reąuire (można też wykorzystać use, ale cgi-lib.pl najczęściej dołącza się za pomocą reąuire). W przeciwieństwie do CGI.pm, w cgi-lib.pl nie istnieje zbyt wiele procedur do tworzenia znaczników HTML, ale w większości przypadków znaczniki te wpisuje się bezpośrednio — głównym zadaniem cgi-lib.pl jest dekodowanie danych przesyłanych do skryptu CGI. Znaczniki HTML tworzy się za pomocą procedur PrintHeader (tworzy nagłówek HTTP) oraz HtmlTop (tworzy znaczniki oraz i umożliwia określenie tytułu), jak na przykład „Moja witryna" poniżej: #!/usr/local/bin/perl require 'cgi-lib.pl'; print &PrintHeader; print &HtmlTop ("Moja witryna"); Kiedy strona jest już zaczęta, dalsze znaczniki HTML wpisuje się bezpośrednio, a dotyczy to także formularzy. Poniżej używamy nagłówka Hl: print "
    Hello!
    "; Do odczytania danych w skrypcie CGI stosuje się procedurę ReadParse, która tworzy asocjację (zwykle nazwaną %in) zawierającą dane przesłane do skryptu, zaś kluczami tych danych są nazwy odpowiednich elementów HTML. Na przykład poniższy fragment kodu tworzy %in i przy założeniu, że dane z pola tekstowego text zostały wysłane, wyświetla te dane: &ReadParse(*in) ; print "Oto wartość text: ", $in{'text'}, "."; Znaczniki powodują wyróżnienie tekstu, co w większości przeglądarek jest interpretowane jako kursywa. Do zakończenia strony znacznikami i używa się procedury HtmlBot (tak naprawdę ta procedura dodaje \n\n): print &HtmlBot; Tak właśnie w skrócie działa cgi-lib.pl. Dalej zajmiemy się szczegółami użycia tego pakietu. Wydruk 19.1. libl.cgi # ! /usr/local/bin/perl reąuire 'cgi-lib.pl1; print SPrintHeader; print &HtmlTop ("Przykład CGI z wykorzystaniem cgi-lib.pl1'); print "" To jest ankieta !

    Prosimy o wypełnienie naszej ankiety...

    Co daje wypełnienie naszej ankiety:
  • Sława
  • Pieniądze
  • Zabawa
Jeśli nie masz ochoty naszej ankiety wypełniać, być może zainteresuje Cię witryna CPAN.
Proszę podać swoje imię: Proszę wpisać tu swoją opinię: Brak zdania Proszę wskazać używane produkty:

Szampon Pasta do zębów Chleb Pociski balistyczne

Proszę określić szacunkowy przychód:

Oto Twoje odpowiedzi...


if (SReadParse(*in)) { print "Na imię masz: ", $in{'text'}» ".", "

", "Twoje zdanie: ", Sin{'textarea'}, ".", "

", "Używasz następujących produktów: ", joinf", ", &SplitParam($in('checkboxes'})), ".", "

", "Poziom przychodów: ", $in{'list')i ".", "

", "Dziś jest ", $in('radios'), ". dzień tygodnia", "

", "Mile widziana ilość poczty: ", $in{'popupmenu'), ".", "

", "Dane ukryte to ", Sin('hiddendata'), ".


"; ) print SHtmlBot; Zagadnienia: Jakie procedury zawiera cgi-lib.pl? Oto procedury zawarte w pakiecie cgi-lib.pl wraz z krótkim opisem ich działania: * CgiDie — ma znaczenie takie samo jak CgiError, czyli wyświetla komunikat błędu, ale oprócz tego kończy działanie programu; * CgiError — wyświetla komunikat błędu z odpowiednimi nagłówkami i znacznikami HTML. Jeśli nie zostaną przekazane żadne parametry, wyświetli się standardowy komunikat błędu. W przeciwnym razie pierwszy parametr to tytuł komunikatu, a następne parametry będą kolejnymi akapitami komunikatu; * HtmlBot — procedura zwraca po prostu \n\n, umieszczane na dole strony; * HtmlTop — zwraca nagłówek strony — — oraz początek sekcji BODY. Jeśli podany zostanie jakiś parametr, cgi-lib.pl użyje go jako tytułu strony oraz doda na górze strony w znaczniku

; * MethGet — zwraca true, jeśli aktualne wywołanie CGI zostało wykonane metodą GET, w przeciwnym razie — f alse; * MethPost — zwraca true, jeśli aktualne wywołanie CGI zostało wykonane metodą post , w przeciwnym razie — f alse; * MyBaseUrl — zwraca bazowy adres URL skryptu CGI bez jakichkolwiek ścieżek i bez treści zapytania; * MyFullUrl — zwraca pełny adres URL skryptu CGI wraz ze ścieżkami i treścią zapytania; * PrintEnv — formatuje i wyświetla zmienne środowiskowe dostępne dla skryptu; * PrintHeader — zwraca następujący tekst: Content-type: text/html\n\n. Tekst ten powinien rozpoczynać wszystkie generowane z CGI strony sieciowe; * PrintVariables — formatuje i wyświetla wartości danych. Można jej przekazać asocjację lub typ ogólny (w tym ostatnim przypadku zostaną wyświetlone dane z odpowiedniej tablicy). Jeśli żadne parametry nie zostaną przekazane, użyta zostanie asocjacja %in; * ReadParse — jest to najważniejsza procedura w cgi-lib.pl. Odczytuje ona i analizuje dane przesłane z formularza HTML do skryptu CGI metodami get lub post. Zwykle procedury tej używa się do utworzenia asocjacji %in — przekazuje się jej w tym celu typ ogólny *in. Asocjacja zawiera dane przesłane do skryptu, przy czym ich kluczami są nazwy elementów graficznych z formularza. Odpowiednie asocjacje wypełniają opcjonalne parametry — drugi, trzeci i czwarty — danymi, które powstają w wyniku ładowania plików. Jeśli dane udało się bezbłędnie przeanalizować, ReadParse zwraca wartość true, zaś w przeciwnym przypadku zwraca wartość f alse; * SplitParam — rozbija parametr zawierający listę wartości na listę pojedynczych parametrów. Procedury tej używa się w przypadku elementów HTML, które mogą zwracać wiele wartości, jak pola opcji. Początek dokumentu HTML Aby zacząć dokument HTML za pomocą pakietu cgi-lib.pl, trzeba uruchomić plik pakietu. W celu uzyskania na stronie potrzebnego nagłówka HTTP, używa się procedury PrintHeader, a następnie za pomocą procedury HtmlTop tworzy się sekcję HEAD to j i początek body. Jeśli HtmlTop otrzyma parametr, przekazany tekst jest używany jako ltu; tytuł strony i umieszczany w nagłówku H l na górze strony. Na przykład w skrypcie libl.cgi początek wygląda następująco: #!/usr/local/bin/perl require 'cgi-lib.pl’; print &PrintHeader; print &HtmlTop(„Przykład CGI z wykorzystaniem cgi-lib.pl"); Wynik działania tego kodu pokazano na rysunku 19.1 we wcześniejszej części tego rozdziału. Jeśli są potrzebne dodatkowe atrybuty w sekcji BODY, można znacznik umieścić dalej: Print "

" Można oczywiście HtmlTop całkowicie pominąć i samemu wpisać sekcje HEAD i BODY. Tworzenie nagłówków HTML Moduł cgi-lib.pl nie zawiera specjalnych procedur do tworzenia znaczników HTML, zatem jeśli mają one być umieszczone na stronie, trzeba wpisać je bezpośrednio. W poniższym przykładzie tworzymy nagłówki

oraz

: print "To jest ankieta!

Prosimy o wypełnienie naszej ankiety ...

" ; Wyniki pokazano na rysunku 19.1 we wcześniejszej części tego rozdziału. Wyśrodkowanie elementów HTML Aby pewne elementy wyśrodkować na stronie HTML, należy po prostu użyć znacznika
: print "

To jest ankieta !

Prosimy o wypełnienie naszej ankiety ...

" ; Wyniki pokazano na rysunku 19.1 we wcześniejszej części tego rozdziału. Tworzenie list HTML Możemy użyć listy wyliczanej, aby przedstawić użytkownikowi korzyści płynące z wypełnienia naszej ankiety: print "Co daje wypełnienie naszej ankiety:

  • Sława
  • Pieniądze
  • Zabawa
; Wyniki działania tego kodu pokazano na rysunku 19.1 we wcześniejszej części tego rozdziału. Tworzenie hiperłączy Aby utworzyć hiperłącze, należy po prostu wstawić kod opisujący takie hiperłącze. Oto sposób tworzenia łącza umożliwiającego użytkownikowi przejście na stronę CPAN: print "Jeśli nie masz ochoty naszej ankiety wypełniać, być może zainteresuje Cię witryna CPAN."; Wyniki działania tego kodu pokazano na rysunku 19. t we wcześniejszej części tego rozdziału. Tworzenie poziomej linii Linie poziome w HTML realizuje znacznik
: Print „
”; To odkreślenie pojawia się na dole rysunku 19.1, wcześniej w tym rozdziale. Tworzenie formularza HTML Pakiet CGl.pm udostępnia metodę startform, ale cgi-lib.pl takiej procedury nie zawiera — formularz trzeba tworzyć samemu. W znaczniku
należy podać dwa parametry: method (metoda przekazywania danych: POST lub get — w przykładzie użyjemy post) oraz action (adres URL skryptu CGI, do którego dane mają zostać wysłane). Można też wskazać sposób kodowania formularza, choć zwykle nie jest to potrzebne: print ""; Po tym, jak w formularzu został umieszczony znacznik , wszystkie elementy graficzne opisywane dalej w kolejnych punktach, aż do punktu wystąpienia znacznika
, są zamknięte w formularzu (zobacz też punkt „Zakończenie formularza HTML" w dalszej części rozdziału). Używanie pól tekstowych Aby utworzyć pole tekstowe, w którym użytkownik może wpisywać dane, używa się znacznika z atrybutem TYPE o wartości text. W skrypcie libl.cgi wykonujemy to następująco —jako wartość początkową przekazujemy puste znaki: print "Proszę podać swoje imię:

" Utworzone tak pole tekstowe pokazano na rysunku 19.2 we wcześniejszej części tego rozdziału. Mamy zatem już w tym polu tekstowym dane, czas więc się zastanowić, w jaki sposób te dane odczytać w następnym skrypcie. Tym zagadnieniem zajmiemy się w następnym punkcie. Odczytywanie danych spod elementów graficznych HTML W celu odczytania danych spod elementów graficznych HTML — używamy procedury ReadParse. Zwykle tworzy się w ten sposób asocjację %in zawierającą te dane. Kluczami są nazwy elementów graficznych. W poprzednim punkcie utworzyliśmy pole tekstowe o nazwie text, zatem teraz możemy zawrzeć dane z tego pola w asocjacji %in: require 'cgi-lib.pl'; if (&ReadParse(*in)) { print "Na imię masz: ", $in{'text'}, „.”, „

”; } Znacznik jest interpretowany przez większość przeglądarek jako nakaz użycia kursywy. Zwróć uwagę na sprawdzenie wartości zwróconej przez ReadParse jeszcze przed skorzystaniem z asocjacji %in —jeśli procedura ta zwróciłaby wartość false, nawet nie próbowalibyśmy odczytywać żadnych danych. Tak właśnie zwykle odczytuje się dane za pomocą cgi-lib.pl: wywołuje się ReadParse, która pobiera dane i wstawia je do asocjacji. Wyniki działania tego kodu można zobaczyć na rysunku 19.4, który znajduje się we wcześniejszej części tego rozdziału. Używanie obszarów tekstowych W celu utworzenia obszaru tekstowego używa się znacznika

"; Wyniki pokazano na rysunku 19.2, wcześniej w tym rozdziale. Kiedy użytkownik kliknie przycisk Submit, dane zostaną przesłane do Iib2.cgi. W celu wyświetlenia zawartości tego obszaru tekstowego używamy następującego kodu: if (&ReadParse(*in)> { print "Twoje zdanie: ", $in{'textarea'}, "."; } Używanie pól opcji W celu utworzenia grupy pól opcji używa się znacznika z atrybutem TYPE o wartości checkbox, zaś wszystkie pola opcji mają tę samą nazwę (wtedy należą one do tej samej grupy): print ' "Proszę wskazać używane produkty:

Szampon Pasta do zębów Chleb Pociski balistyczne

"; Wyniki pokazano na rysunku 19.3, który znajduje się wcześniej w tym rozdziale. Zwróć uwagę, że pola opcji pozwalają wybrać więcej niż jedną opcję. Kiedy dane z grupy pól opcji zostaną przesłane skryptowi Hb2.cgi, wyrażenie $in{'checkboxes'} zwróci tekst, który zawiera wiele wartości. W celu utworzenia listy na podstawie tego tekstu ;ze używa się procedury SplitParam: if (&ReadParse(*in)) { print "Używasz następujących produktów: ", join(", ", &SplitParam($in{'checkboxes'})), "."; } Wyniki działania tego kodu pokazano na rysunku 19.4 — widzimy wyświetlone wszy-1 stkie wybory użytkownika porozdzielane przecinkami. Używanie list wyboru Listę wyboru tworzy się za pomocą znacznika

" ; Można też wskazać liczbę pozycji, które mogą się pojawiać jednocześnie, za pomocą atrybutu size. Wyniki działania powyższego kodu pokazano na rysunku 19.3, wcześniej w tym rozdziale. Sposób odczytywania danych w Iib2.cgi i ich wyświetlenia przedstawiono poniżej: if (&ReadParse(*in)) { "Poziom przychodów: ", $in{'list'}, ".", "

"; } Używanie przycisków radio Do utworzenia przycisków radio używa się znaczników z atrybutem TYPE o wartości radio. Aby przyciski zgrupować, należy nazwać je tak samo. Oto sposób utworzenia przycisków radio, które umożliwiają wskazanie dnia tygodnia. Pierwszy przycisk ma atrybut checked, co powoduje, że ten właśnie przycisk jest wybierany domyślnie: print "Proszę podać dzień tygodnia:

Niedziela Poniedziałek Wtorek Środa Czwartek Piątek Sobota" ; Wyniki działania tego kodu pokazano na rysunku 19.3. Oto sposób odczytania wybranej wartości w skrypcie Iib2.cgi (rezultat pokazano na rysunku 19.4): if (&ReadParse(*in)) { print "Dziś jest " $in{'radios1}, ". dzień tygodnia"; } Używanie menu Menu rozwijane HTML to lista przewijana, w której widoczna jest tylko jedna pozycja na raz. Tworzy się ją za pomocą znacznika

" ; Wyniki działania tego kodu są widoczne na rysunku 19.3. Oto sposób odczytania i wyświetlenia wartości wybranej przez użytkownika: if (&ReadParse(*in)) { print "Mile widziana ilość poczty: ", $in{'popupmenu1}, ".", "

"; } Użycie pól ukrytych Do utworzenia ukrytych elementów graficznych służących do przetwarzania danych, trzeba użyć znacznika z atrybutem type o wartości hidden. Oto sposób utworzenia takiego pola o nazwie hiddendata i wartości Różany pączek: print "" ; Skrypt Iib2.cgi odczytuje i wyświetla te dane (rysunek 19.4): if (&ReadParse (*in)) { print "Dane ukryte to " , $in{ 'hiddendata' }, "."; } Tworzenie przycisków SUBMIT i RESET W celu utworzenia przycisku Submit stosuje się znacznik z atrybutem TYPE o wartości submit, tymczasem do utworzenia przycisku Reset wykorzystuje się też znacznik , ale z atrybutem TYPE o wartości reset: print "

" ; Wyniki pokazano na rysunku 19.3. Kiedy użytkownik kliknie przycisk Submit, dane z formularza są przesyłane do skryptu Hb2.cgi, Jeśli natomiast użytkownik kliknie przycisk Reset, dane, które znajdują się w formularzu, są usuwane. Zakończenie formularza HTML Formularz HTML możemy zakończyć, dodając po prostu znacznik : print "" ; Kiedy skrypt libl.cgi przekaże znacznik , zostanie zakończony formularz, który łączy wszystkie tworzone dotychczas elementy graficzne. Teraz zostało już tylko zakończyć całą stronę HTML. Zakończenie dokumentu HTML Jeśli używa się pakietu cgi-lib.pl, do zakończenia strony można użyć procedury HtmlBot zwracającej tekst \n\n: print &HtmlBot; Często tak właśnie wygląda ostatni wiersz całego skryptu CGI. W ten sposób zakończyliśmy oba skrypty — libl.cgi oraz Iib2.cgi. Wyświetlanie wszystkich zmiennych W tym rozdziale wyświetlaliśmy dane zawarte w poszczególnych elementach graficznych HTML, ale można to zrobić również prościej: jeśli postać wyniku nie jest istotna, procedura PrintYariables pozwala wyświetlić od razu wszystkie wartości. Możemy na przykład zastąpić cały Iib2.cgi następującym fragmentem kodu: #!/usr/local/bin/perl5 require 'cgi-lib.pl'; print &PrintHeader; print &HtmlTop ("Przykład CGI z wykorzystaniem cgi-lib.pl"); if (&ReadParse(*in)) { print &PrintVariables; } print 4HtmlBot; Wyniki pokazano na rysunku 19.5. Wszystkie dane wysłane do Hb2.cgi są faktycznie wyświetlane, ale brakuje jakichkolwiek objaśnień. Procedura PrintYariables jest pomocna przy usuwaniu błędów z kodu, ale zwykle nie nadaje się do zastosowania w gotowym produkcie. Rozdział 20. CGI: liczniki odwiedzin, księga gości, wysyłanie listów, kwestie bezpieczeństwa W skrócie W tym rozdziale i w rozdziale następnym zajmiemy się przykładowymi skryptami CGI: licznikiem odwiedzin strony WWW, księgą odwiedzin na stronie, skryptem do wysyłania poczty elektronicznej, pokojami pogawędek (chat), obsługą cookies, grami i innymi. Opisane skrypty CGI możesz dowolnie dostosowywać do swoich potrzeb. Pamiętaj, że prezentowane tu skrypty służą jako przykład i jeśli mają być instalowane na komputerze widocznym w Internecie, należy dodać do nich obsługę błędów oraz uwzględnić kwestie bezpieczeństwa, a po skończeniu pracy nad nimi należy koniecznie sprawdzić, czy działają zgodnie z wymaganiami. Internet udostępnia już mnóstwo skryptów Perla, a oto kilka przykładowych adresów, skąd można je pobrać (oczywiście konieczne jest —jak zwykle — sprawdzenie, w jaki sposób ich autorzy poradzili sobie z bezpieczeństwem i innymi tego typu kwestiami): * perlowe archiwum Jasona: www.aquapal.co.uk/per/perl.html; * archiwum skryptów Matta: www.worldwidemart.com/scripts/; * skrypty perłowe Yahoo: dir.yahoo.com/Computers_andJnternet/Programming_Languages/Perl/Scripts/; * skrypty perłowe i hiperłącza Dale'a Bewley'ego: www.bewley.net/perl/; * strona CGI na witrynie www.perl.com: reference.perl.com/query.cgi?cgi. Przy tworzeniu skryptów bardziej złożonych, niż proste skrypty opisane w dwóch ostatnich rozdziałach, istotną sprawą staje się bezpieczeństwo. Tym tematem zajmiemy się w niniejszym rozdziale już całkiem poważnie. Bezpieczeństwo CGI Bezpieczeństwa nigdy nie wolno lekceważyć, a obecnie jest to szczególnie istotne, gdyż systemy operacyjne stają się coraz bardziej skomplikowane i coraz trudniej jest uwzględnić wszystkie istniejące w nich luki zabezpieczeń. W systemach Unix skrypty CGI działają z identyfikatorem użytkownika nobody, co oznacza, że nie otrzymują wielu uprawnień — zasada jest taka, że im mniejsze są ich uprawnienia, tym mniej szkody mogą ewentualnie wyrządzić. Mimo to wiele złego może wyniknąć z niestarannego zakodowania skryptów CGI i w tym rozdziale pokażemy właśnie, jak unikać niektórych zagrożeń. Oto zestaw tych stron o bezpieczeństwie w perłowych skryptach CGI, które na pewno należy przeczytać przedutworzeniem pierwszego bardziej złożonego, ogólnie dostępnego skryptu: * strona World Wide Web Consortium poświęcona bezpieczeństwu CGI: www.w3.org-/Security/Faq/www-security-faq.html; * poświęcony bezpieczeństwu dział FAQ perłowych CGI: www.perl.com/CPAN-local/doc/FAQs/cgi/perl-cgi-faq.html; * strona Seleny Soi poświęcona zagrożeniom wynikającym z instalowania gotowych skryptów: Stars.com/Authoring/Scripting/Security/; * FAQ bezpieczeństwa CGI Paula Phillipsa: www.go2net.com/people/paulp/cgi-security/safe-cgi.txt (choć ta strona zawiera wiele ważnych uwag, trzeba pamiętać, że nie była aktualizowana od roku 1995). Teraz możemy zająć się już kodowaniem — opiszemy kwestie bezpieczeństwa oraz realizację liczników odwiedzin i księgi gości, a także omówimy wysyłanie poczty elektronicznej ze strony. Zagadnienia: Bezpieczeństwo na poważnie Ze skryptami CGI wiąże się wiele różnych zagrożeń. W skrajnym przypadku można byłoby utworzyć skrypt wywołujący programy, których nazwy byłyby przekazywane jako parametry. Dane z formularza HTML są przesyłane jako tekst, w którym ogranicznikami parametrów są znaki zapytania. Taki tekst jest dołączany na koniec adresu URL, zatem jeśli chcesz po prostu sprawdzić działanie skryptu, odpowiedni URL może wyglądać następująco: http://www.serwer.com/user/perl.exe?skryptl.pl Jeśli włamywacz zauważy takie wywołanie, bardzo łatwo.może użyć własnych poleceń: http://www.serwer.com/user/perl.exe?-e+'groźne polecenia' W ten sposób można uruchamiać dowolne polecenia Perla, co nie jest najlepszym pomysłem. Przykład ten pokazuje jedną z największych luk bezpieczeństwa, które występują w skryptach CGI — wywoływanie programów zewnętrznych bez sprawdzenia przekazywanego im kodu. Perl pozwala wywoływać programy zewnętrzne na wiele sposobów. Można zastosować odwrotne apostrofy, otworzyć potok do innego programu, ewentualnie użyć wywołania system lub exec. Nawet instrukcja eval musi być traktowana z należytą ostrożnością. Bardzo ważne jest takie przygotowanie interfejsu CGI, aby uniemożliwić przypadkowe wykonanie niebezpiecznych poleceń. Sieciowi włamywacze to specjaliści w odnajdowaniu tego typu luk w zabezpieczeniach i używaniu Twoich skryptów do wykonywania ich własnego kodu. Perl zawiera gotowy mechanizm zabezpieczeń pozwalający radzić sobie z opisanymi problemami (zajrzyj do punktu „Użycie błędnych danych" w dalszej części tego rozdziału). Kiedy zostanie włączona ochrona, niemożliwe jest przekazywanie z zewnątrz jakichkolwiek danych takim poleceniom, jak na przykład system czy exec. Podstawowa zasada to nieprzekazywanie niesprawdzonych danych programom zewnętrznym oraz staranne blokowanie możliwości uruchomienia interpretera poleceń. Rzadko zdarzają się sytuacje, kiedy nie można uniknąć użycia interpretera poleceń. Jeśli jednak tak się zdarzy, trzeba zawsze sprawdzać wszystkie przekazywane interpreterowi dane pod kątem występowania w nich metaznaków i przynajmniej je usuwać. Oto meta-znaki zwykłego interpretera poleceń systemu Unix: &;'`\"|*?~<>" ()[]{}$\n\r Zwróć uwagę na następną ważną sprawę: nie pozwalaj nikomu poprawiać swoich skryptów ani plików z danymi. Musisz być szczególnie ostrożny, określając uprawnienia do plików, gdyż musisz to zrobić w taki sposób, aby jedni nie mogli nadpisywać danych wprowadzonych przez kogoś innego. Obowiązują również oczywiście zwykłe zasady bezpieczeństwa: nie należy wysyłać haseł pocztą elektroniczną czy też wpisywać ich za pomocą publicznie dostępnych narzędzi, jak na przykład Uniksowego ytalk. Nie należy pozostawiać długo nieużywanych kont (sieciowi włamywacze czyhają na nie, aby je przejąć). Nie pozwól, aby stosowane skrypty CGI ujawniały zbyt wiele informacji o systemie — niebezpieczeństwo czyha tam, gdzie się go nie spodziewasz. Użycie błędnych danych Jedną z największych luk w bezpieczeństwie skryptów CGI jest przekazywanie niesprawdzonych danych interpreterowi poleceń. W Perlu można użyć specjalnego mechanizmu ochrony przed danymi potencjalnie niebezpiecznymi, które określa się mianem błędnych danych. Kiedy mechanizm ten zostanie włączony, wszystkie zmienne, które zawierają dane przekazywane spoza programu (w tym także dane przekazane ze środowiska, ze standardowego wejścia i wiersza poleceń), są uważane za błędne (uszkodzone). Danych z takim statusem nie można przekazywać nigdzie poza program. Jeśli wartość jednej zmiennej z błędnymi danymi przypisana zostanie innej zmiennej, to ta druga zmienna od tej chwili także uważana jest za uszkodzoną. W związku z tym omówione dane mogą swobodnie krążyć po programie, ale cały czas są oznaczone jako błędne. Uszkodzenie danych jest związane z danymi skalarnymi. Oznacza to, że niektóre elementy tablicy mogą zostać uznane za błędne, podczas gdy pozostałe elementy tej samej tablicy już nie. Warto zauważyć, że błędne zmienne nie mogą być użyte w instrukcjach eval, system, exec, a także nie mogą być stosowane w potokach. Perl dba również o to, aby tak oznaczone dane nie zostały użyte w żadnym poleceniu wywołującym podinterpreter poleceń ani modyfikującym pliki, katalogi czy procesy. Jest jednak jeden ważny wyjątek: jeśli lista parametrów zostanie przekazana poleceniu system lub exec, wówczas elementy te nie są sprawdzane pod kątem uszkodzenia. Jeśli spróbujesz w jakikolwiek sposób przekazać poza program błędne dane, Perl zakończy swoje działanie, zwracając komunikat ostrzeżenia, co oznacza, że skrypt CGI po prostu zostanie przerwany. Jeśli wykrywanie zagrożeń zostanie włączone, Perl przerywa działanie także przy wywołaniu zewnętrznego programu bez jawnie ustawionej zmiennej środowiskowej PAT H. W Perlu 4 ochronę przed uszkodzonymi danymi włączało się, stosując specjalną wersję interpretera Perla, taintperl: #!/usr/local/bin/taintperl Jednak w wersji 5 ochrona została już wbudowana do standardowego interpretera, dlatego włącza się ją, używając przełącznika-T #!/usr/local/bin/perl -T Oto przykład, w którym włączamy ochronę, ale jako że nie robimy nic niebezpiecznego, nic szczególnego się nie dzieje: #!/usr/local/bin/perl -T print "Hallo!\n"; Hello! Jeśli zostanie jednak wywołane niebezpieczne polecenie, takie jak system, Perl poinformuje o ewentualnej luce w bezpieczeństwie, związanej z danymi ze środowiska. Nawet jeśli przy wywoływaniu programu zewnętrznego nie skorzysta się ze ścieżki (path), to zawsze istnieje szansa, że może zechcieć to zrobić wywoływany program. Oto komunikat błędu, jaki się pojawi: #!/usr/local/bin/perl -T print system('date'); Insecure $ENV{PATH} while running with -T switch at taint.cgi line 5,<> chunk 1. Aby poradzić sobie z tym problemem, można jawnie ustawić wartość $ENV{'PATH'}: #!/usr/local/bin/perl -T $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin'; print system('date'); Thu Nov 12 19:55:53 EST Oto kolejny przykład, w którym próbujemy wywołać polecenie system, korzystając z błędnych danych. Mimo że ustawiono $ENV {'PATH'}, działanie skryptu jest przerywane, gdyż próbuje się przekazać poleceniu systemowemu błędne dane: #!/usr/local/bin/perl -T $ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin’; while (<>) { $command = $_; system($command); } Insecure dependency in system while running with -T switch at taint.cgi linę 5, <> chunk 1. Mimo że dane przepisano ze zmiennej $_ do $command, to nadal są one uważane za błędne. Jeśli jednak jakichś danych jesteś całkiem pewien, można nakazać traktować je jako poprawne, co opisano w następnym punkcie. Korygowanie danych Jednym sposobem usunięcia ze zmiennej piętna uszkodzenia jest użycie wzorców do wycięcia części jej wartości. Oto przykład, w którym zakładamy, że w zmiennej $tainted znajduje się adres e-mail. Adres ten możemy pobrać i zapisać w bezbłędnej zmiennej następująco: $tainted =~ /(^[\w]+\@([\w.]+)/; $username = $1; $domain = $2; print "$username\n"; print "$domain\n"; Pobraliśmy z uszkodzonej zmiennej dane, których możemy już być pewni. W ten właśnie sposób koryguje się dane, wycinając takie ich części, co do których nie ma się już wątpliwości (w ten sposób można pozbyć się metaznaków interpretera poleceń). Zwiększanie uprawnień skryptu CGI w systemie UNIX Jako że skrypty CGI są uruchamiane w systemie Unix z konta nobody, ich uprawnienia są niewielkie. Czasem może być przydatne rozszerzenie tych uprawnień, aby skrypt mógł wykonywać jakieś dodatkowe operacje, na przykład tworzyć pliki. Uprawnienia skryptów CGI można rozszerzyć, ale przedtem należy rozważyć wszystkie alternatywne możliwości i dopiero wtedy — z dużą dozą ostrożności — zwiększać uprawnienia skryptu. Skrypt Perla może być uruchamiany jako suid, co da mu takie same uprawnienia, jakie ma właściciel (czyli zwykle Ty, Czytelniku). Aby jednak coś takiego wykonać, powody muszą być naprawdę ważkie, a poza tym te rozszerzone uprawnienia należy odebrać, kiedy tylko nie będą już potrzebne. Aby skrypt był uruchamiany w trybie suid, należy za pomocą polecenia chmod ustawić mu bit s: chmod u+s skryptl.pl Można też uruchamiać skrypt z uprawnieniami grupy właściciela skryptu, w tym celu należy ustawić bit s dla grupy: chmod g+s skryptl.pl Niektóre systemy Unix zawierają niewielką lukę w systemie bezpieczeństwa, która powoduje, że skrypty uruchamiane w trybie suid mogą być stosowane w niewłaściwych celach. Jeśli używany system należy do tych niebezpiecznych, w przypadku próby uruchomienia skryptu jako suid — Perl zgłosi błąd. Większość zadań można wykonać całkowicie bezpiecznie, nie uciekając się do uruchamiania skryptów jako suid. Niniejszy punkt został włączony do wykładu jedynie po to, aby zachować kompletność opisu. Jeśli jednak zdecydujesz się na takie ustawienie uprawnień do skryptów CGI, musisz wiedzieć dokładnie, co robisz. Poza tym musisz pamiętać, że nie można takich skryptów zostawiać bez dozoru. Tworzenie licznika odwiedzin Tworzenie skryptu licznika odwiedzin nie jest skomplikowane, wystarczy zapisać bieżącą wartość licznika w pliku i wyświetlać ją w miarę potrzeb. Nasz przykładowy licznik nazwiemy counter.cgi. Zaprezentowany licznik po prostu wyświetla bieżącą wartość jako ciąg znaków, choć nic nie stoi na przeszkodzie, aby uzyskać efekty znacznie ciekawsze. Można na przykład utworzyć licznik graficzny, korzystając z zestawu plików z obrazami cyfr wyświetlanych obok siebie na stronie. Do wyświetlenia poszczególnych cyfr można użyć znacznika , którego atrybut SRC będzie miał wartość określającą adres URL odpowiedniego skryptu pokazującego te cyfry. Skrypt licznika pokazano na wydruku 20.1. Aby go użyć, trzeba w tym samym katalogu, w którym znajduje się skrypt, umieścić plik counter.dat. Na początek w pliku tym umieszcza się liczbę O (po prostu za pomocą edytora tekstowego), a następnie należy dla pliku określić takie uprawnienia, aby skrypty CGI mogły ten plik modyfikować. Nie należy tworzyć pliku counter.dat w skrypcie, gdyż standardowo system nie pozwala skryptom CGI na tworzenie plików w katalogach użytkowników. Wydruk 20.1. counter.cgi #!/usr/bin/perl use CGI; $co = new CGI; open (COUNT, "; close COUNT; $count++; open (COUNT, ">counter.dat"); print COUNT $count; close COUNT; print $co->header, $co->start_html( -title=>’Przykładowy licznik’, -author=>'Steve', -BGCOLOR=>'white', ), $co->center($co->h1('Przykładowy licznik’)), $co->p, $co->center($co->h3("Wartość bieżąca: ", $count)), $co->p, $co->center ($co->h3("Licznik jest aktualizowany po przeładowaniu strony")), $co->end_html; Skrypt ten jest bardzo prosty: jedynie odczytuje liczbę z pliku counter.dat, zwiększają, zapisuje z powrotem w counter.dat i wyświetla zwiększoną wartość licznika. Wyniki pokazano na rysunku 20. l. Tworzenie księgi gości Tworzenie księgi gości jest nieco trudniejsze od tworzenia licznika odwiedzin, opisanego w poprzednim punkcie. Księga taka służy do zbierania opinii gości, a opinie te są zapisywane w pliku — zwykle HTML — i na żądanie wyświetlane. Księga gości zawiera trzy pliki — wszystkie domyślnie znajdują się w jednym katalogu: guestbookhtm (wydruk 20.2), guestbook.cgi (wydruk 20.3) oraz book.htm (wydruk 20.4). Plik guestbook.htm to strona umożliwiająca użytkownikom wpisanie się do księgi gości. Od użytkownika pobiera się imię i komentarz (rysunek 20.2). Kiedy użytkownik kliknie przycisk Submit, dane są wysyłane do skryptu guestbook.cgi (jeśli chcesz użyć tego skryptu, musisz w guestbook.htm zmienić ogólny adres tak, aby wskazywał Twój skrypt): Dodaj do książki odwiedzin...

W skrypcie guestbook.cgi otwieramy samą księgę gości (odwiedzin), zapisaną w pliku book.htm — chcemy w niej dopisać na końcu nowe imię i komentarz, ale przecież book.htm zwykle kończy się znacznikami , zatem najpierw konieczne będzie przesunięcie wskaźnika położenia w pliku przed te znaczniki: open (BOOK, ">>book.htm") or die "Otworzenie księgi odwiedzin było niemożliwe."; seek (BOOK, -length($co->end_html), 2); * Jako że obiekt cgi do utworzenia znaczników używa metody end_html, przesuniemy się od końca wstecz dokładnie o długość tekstu zwracanego przez tę metodę. W ten sposób uchronimy się przed sytuacją, w której end_html będzie zwracać jakiś inny tekst, jak w nowych wersjach Perla. Następnie w miejsce znaczników wpisujemy nową treść, zatem na koniec znów musimy skorzystać z metody end_html, aby te znaczniki dodać. Kiedy już imię i komentarz nowego gościa zostaną dopisane do księgi, guestbook.cgi tworzy stronę pokazaną na rysunku 20.3, dziękując użytkownikowi za komentarz i wyświetlając hiperłącze, które umożliwia obejrzenie zawartości księgi. Jeśli zatem chcesz użyć tego skryptu, musisz zmienić ogólny adres URL w guestbook.cgi, aby wskazywał Twój plik book.htm (należy przy tym sprawdzić, czy book.htm ma dostatecznie niskie uprawnienia, aby skrypty CGI mogły go modyfikować): "Jeśli chcesz do tej księgi zajrzeć, ", $co->a( {href=>"htpp://www.serwer.com/user/cgi/book.htm"}, "kliknij tutaj" ), „.”, Kiedy użytkownik kliknie hiperłącze, zobaczy księgę odwiedzin z pliku book.htm (rysunek 20.4). Oczywiście to samo hiperłącze można także umieścić na innych stronach. W książce są pokazywane imiona użytkowników wraz z datami wpisania się. Skrypt guestbookcgi powoduje, że żaden kod HTML wprowadzony przez użytkownika nie zostanie zinterpretowany — wszystkie znaki < są zamieniane na encję sit (odpowiedni fragment kodu to $username =~ s/), tło i tak dalej —jak na każdej innej stronie HTML. Trzeba jedynie pamiętać, aby na samym końcu tego pliku znalazły się znaczniki (lub inny tekst generowany przez metodę end_html).Dzięki temu skrypt guestbookcgi rozpozna, o ile bajtów ma się cofnąć, aby kolejne wpisy znalazły się w odpowiednim miejscu. Wydruk 20.2. guestbook.htm Wpis do księgi odwiedzin

Proszę wpisać się do księgi odwiedzin. ..


Proszę podać imię:



Wydruk 20.3. guestbook.cgi #!/usr/bin/perl use CGI; $co = new CGI; open (BOOK, ">>book.htm") or die "Otwarcie książki odwiedzin nie powiodło się."; seek (BOOK, -length($co->end_html), 2); $date = 'date’; chop($date); $username = $co->param('username'); $username =~ s/param('comments'); $text =~ s/h3( "Nowy komentarz z dnia ", $date, ", autor: ", $username, $co->p, $text, ), $co->hr, $co->end_html; close BOOK; print $co->header, $co->start_html( -title=>'Przykładowa księga odwiedzin', -author=>'Steve', -BGCOLOR=>'white', -LINK=>'red' ); print $co->center ($co->hl (' Dziękujemy za wpisanie się do księgi!’)) "Jeśli chcesz do tej księgi zajrzeć, ", $co->a ( {href=>"htpp://www.serwer.com/user/cgi/book.htm" ), "kliknij tutaj" ), „.”, $co->hr, $co->end html; Wydruk 20.4. boot.htm Księga gości

Oto księga gości ...


Użycie skryptów CGI do wysyłania poczty Opinie użytkownika można przechowywać na komputerze dostawcy Internetu, jak to robiliśmy w poprzednim przykładzie z książką telefoniczną, ale czasem wygodniej byłoby otrzymywać opinie za pośrednictwem poczty elektronicznej. Tym właśnie zajmuje się następny skrypt. Aby móc wysłać pocztę, konieczne jest jednak skorzystanie z zasobów systemowych, wobec czego skrypt ten jest zależny od stosowanego systemu operacyjnego. W tym przypadku zakładamy, że skorzystamy z systemu Unix. Aplikacja pocztowa składa się z pliku HTML, email.htm, który jest interfejsem umożliwiającym użytkownikowi wpisanie wiadomości bezpośrednio na stronie, co pokazano na rysunku 20.5. Mamy też skrypt CGI, email.cgi, który odbiera dane, wysyła wiadomość, a użytkownikowi pokazuje potwierdzenie (rysunek 20.6). Plik email.htm pokazano poniżej na wydruku 20.5, zaś email.cgi na wydruku 20.6. Nasza wiadomość e-mail jest wysyłana tak, jak każda inna — na przykład po otrzymaniu wiadomości z rysunku 20.5 dostałbyś następującą informację (zwróć uwagę, że użytkownik sam podaje swój adres, zatem trzeba się liczyć z tym, że może to nie być prawdziwy adres): Date: Thu, 12 Nov 15:26:57 -0500 (EST) To: steve@serwer.com From: user@serwer.com Subject: Pozdrowienia Witam! Jak się masz? Napisz, kiedy będziesz miał chwilę czasu. A. F. Użytkownik Opisywana aplikacja pozwala użytkownikom wysyłać pocztę elektroniczną do autora strony. Nie jest konieczne tworzenie jakichkolwiek zapisów na serwerze dostawcy usług internetowych. Podczas dostosowywania tej aplikacji do swoich potrzeb nie można zapomnieć o zamianie w pliku email.htm adresu skryptu email.cgi na właściwy:
Konieczne jest też podanie w skrypcie email.cgi poprawnej ścieżki do programu wysyłającego pocztę (w systemach Unix jest to zwykle /usr/lib/sendmail, jak to ustawiono w przykładzie): $text = $co->param('text'); $text =~ s/ Wyślij mi list

Wyślij mi list!


Proszę podać swój adres e-mail:

Proszę podać temat wiadomości:

Proszę podać treść listu:


Wydruk 20.6. email.cgi #!/usr/bin/perl use CGI; $co = new CGI; print $co->header, $co->start_html( -title=>'Przykład wysyłania listu’, -author=>'Steve', -BGCOLOR=>'white', -LINK=>'red' ); if ($co->param()) { $from = $co->param('name') $from=~ s/@/\@/; $subject = $co->param('subject'); $text = $co->param('text'); $text — s/center($co->hl('Dziękuję za list!’)), $co->hr, $co->end html; Rozdział 21. Pogawędki (chat), cookies i gry W skrócie W tym rozdziale pokażemy kilka złożonych przykładów skryptów CGI: aplikację do pogawędek wielu użytkowników jednocześnie, skrypt pozwalający ustawiać i odczytywać cookies oraz grę na tyle poważną, że zabawa z nią może być wciągająca. Pamiętaj, że prezentowane tu skrypty służą jako przykłady i jeśli mają być instalowane na komputerze widocznym w Internecie, należy dodać do nich obsługę błędów oraz uwzględnić kwestie bezpieczeństwa, a po skończeniu pracy nad nimi trzeba koniecznie sprawdzić, czy działają zgodnie z wymaganiami. Aplikacje do pogawędek Zadaniem aplikacji obsługujących chat jest umożliwienie jednoczesnej rozmowy wielu użytkownikom. To, co wpisze jeden użytkownik, widoczne jest dla wszystkich innych, dzięki czemu możliwa jest rozmowa za pośrednictwem Internetu. Aplikacje takie w zasadzie łatwo jest tworzyć, gdyż wystarczy w jednym pliku centralnym zapisywać informacje od wszystkich użytkowników i zarazem wszystkim użytkownikom okresowo pokazywać te informacje w ich przeglądarkach. Są tu jednak pewne pułapki — choćby to, że wielu użytkowników musi używać tego samego pliku, dlatego w celu uniknięcia konfliktów należy stosować blokowanie pliku. W tym rozdziale zaprezentujemy prostą, ale działającą aplikację do pogawędek sieciowych, która pokazuje, jak wygląda programowanie CGI w pełnym wydaniu, jakie stawia wymagania programistom i — w końcu —jak te wymagania spełnić. Cookies Ustawianie i odczytywanie cookies stało się w Internacie bardzo powszechne. Niektórzy użytkownicy niechętnie jednak patrzą na cookies na swoich komputerach, więc trzeba pamiętać, że jeśli użytkownik sobie tego nie życzy, to przykład z tego rozdziału żadnych danych u niego nie zapisze. Pokazany tu skrypt zapisuje imię i datę urodzin osoby, a przy następnych wizytach wita gościa jako znajomego i — jeśli jest ku temu okazja — składa życzenia urodzinowe. Skrypt zapisuje dane w asocjacji, więc łatwo będzie go dostosować do własnych potrzeb. Gra Skrypt z grą prezentowany w tym rozdziale jest pełną wersją gry podobnej do słynnego „wisielca", w której należy rozpoznać słowo, odgadując jego litery. Interfejs tej wersji gry jest dość bezpieczny, gdyż został tak napisany, że nie jest przyjmowany żaden tekst wpisywany bezpośrednio przez użytkownika, lecz wyboru dokonuje się za pośrednictwem przycisków radio. Jeśli użytkownik nie odgadnie słowa i pięciokrotnie nietrafnie wybierze literę, skrypt poda odpowiedź. Skrypt umożliwia zastosowanie (opcjonalnych) obrazków, które odpowiadają poszczególnym liczbom pomyłek, dzięki czemu można stopniowo pokazywać dość upiorny, lecz zgodny z tradycją wizerunek wisielca (jeśli nie zostaną podane pliki graficzne, skrypt jest dość elastyczny, aby się bez nich obejść). Po krótkim opisie funkcji realizowanych przez omawiane w tym rozdziale skrypty możemy zająć się kodem. Instalując te skrypty na komputerze dostawcy Internetu trzeba pamiętać o jednej rzeczy: zarówno te skrypty, jak i sam Pakiet CGI.pm wymagają Perla w wersji co najmniej 5. W niektórych systemach domyślnie są stosowane starsze wersje Perla — w przypadku systemu Unix można zmienić wiersz #! /usr/bin/perl na # ! /usr/bin/per!5 lub podobny. Zagadnienia: Aplikacja do pogawędek Pokazana tutaj aplikacja pozwala zrealizować pokój pogawędek bez korzystania z Javy, JavaScriptu, wtyczek czy innych tego typu „wynalazków". Aplikacja ta w większości przeglądarek powinna działać poprawnie. Skrypt obsługuje wielu użytkowników piszących jednocześnie, zaś to, co użytkownicy piszą, jest widoczne dla innych. W ten sposób możliwa staje się jednoczesna rozmowa wielu osób za pośrednictwem ekranu. Pokoje pogawędek zwykle powodują duży ruch na stronie, gdyż aplikacje takie bazują na wielokrotnym wyświetlaniu stale aktualizowanej strony. Nie wszyscy dostawcy Internetu potrafią docenić użytkowników zajmujących tak duży zakres pasma. Jednym ze sposobów ograniczenia swoich wymagań jest wydłużenie okresu między kolejnymi odświeżeniami ekranu (więcej informacji na ten temat znajduje się w punkcie „Określenie częstości odświeżania strony HTML"). Aplikacja do pogawędek, omawiana w tym rozdziale, jest pokazana na rysunku 21.1. Jak widać, użytkownik podaje swoje imię i komentarz na zwykłej stronie HTML. Po kliknięciu przycisku Wyślij tekst wpisany tekst jest wysyłany na stronę WWW i pokazuje się wraz z imieniem autora w przeglądarkach wszystkich uczestników rozmowy. Wszystko, co jest potrzebne do udziału w konwersacji, to przeglądarka (taka, która jest w stanie obsłużyć znacznik meta z opisem częstotliwości odświeżania — a potrafi to już większość przeglądarek). Wystarczy w przeglądarce otworzyć stronę chat.htm, a resztą zajmie się już aplikacja (rysunek 21.1). Kwestie bezpieczeństwa związane z dostępem wielu użytkowników W tym przykładzie konieczne jest rozważenie kilku kwestii związanych z bezpieczeństwem — co na przykład robić, kiedy któryś z rozmówców zacznie wpisywać bezpośrednio kod HTML. Skrypt przetwarza cały wprowadzony tekst, podmieniając występujące w nim znaki < odwołaniem <, dzięki czemu ewentualne znaczniki nie są interpretowane, lecz są po prostu wyświetlane. Z uwagi na to, że wielu użytkowników jednocześnie sięga do tych samych plików z danymi, skrypt za pomocą funkcji flock blokuje pliki podczas ich odczytywania lub zapisywania, aby uniknąć ewentualnych konfliktów. Nie jest realizowana blokada dzielona, lecz blokada na wyłączność (nawet przy odczycie), a to dlatego, że jest to najbezpieczniejszy sposób działania w różnorodnych systemach, czego dowiodła praktyka (niektóre systemy nie obsługują prawidłowo blokad dzielonych). Po założeniu na plik blokady na wyłączność za pomocą funkcji flock — żaden program Perla ani inny proces nie może na zablokowany plik założyć własnej blokady. Nie musi to wcale oznaczać, że inne programy nie mogą używać pliku (w systemie Unix dostęp jest możliwy) — po prostu od chwili założenia blokady żadne wywołanie funkcji flock nie zwróci wartości true. W skrypcie funkcja f lock jest używana do synchronizowania dostępu do pliku przez uczestników rozmowy, co oznacza, że kolejni użytkownicy — w razie zablokowania tego pliku przez kogoś innego — muszą poczekać na jego zwolnienie. Jeśli skrypt nie może sięgnąć do plików z danymi z powodu ich zablokowania, ponawia próby dostępu dziesięć razy na sekundę przez pięć kolejnych sekund. Jeśli mimo to dostępu nie uda się uzyskać, musi to oznaczać jakiś problem, wobec czego wyświetla się komunikat o przeciążeniu serwera. Odpieranie ataków DOS Atak DOS (Denial of Service, odmowa dostępu) — zgodnie ze swoją nazwą— uniemożliwia użytkownikom sięgnięcie do atakowanych zasobów. Jedną z najbardziej powszechnych metod realizacji tego ataku jest przeciążanie atakowanego systemu, co w przypadku CGI.pm może mieć miejsce podczas przesyłania dużych ilości danych skryptowi lub przy ładowaniu dużych plików. Aby w pewnym stopniu ograniczyć możliwość przeprowadzenia takiego ataku, należy nadać zmiennej $CGi: : post_max wartość nieujemną. Zmienna ta określa maksymalną wielkość przesyłek. Chat nie został napisany z myślą o odmawianiu komukolwiek dostępu. Jeśli jest to z jakiś względów potrzebne, trzeba dołączyć do niego kontrolę hasła. Pogawędki w przeglądarce Teraz opiszemy sposób działania aplikacji. Kiedy użytkownik otworzy w przeglądarce stronę chat.htm, strona ta zawiera dwie ramki. W ramce górnej skrypt chat Legi wyświetla dotychczasowy przebieg konwersacji, a w ramce dolnej skrypt chat2.cgi wyświetla obszar tekstowy, w którym użytkownik może wpisywać swoje wypowiedzi i wysyłać je, klikając przycisk Submit. W górnej ramce użyto znacznika meta żądającego jej odświeżania co pięć sekund. Aby zainstalować aplikację, trzeba umieścić w jednym katalogu pliki chat.htm, chat Legi, chatl.cgi oraz dwa pliki z danymi — chatl.dat i chat2.dat. Plik chat.htm pokazano poniżej na wydruku 21.1, chat.cgi przedstawiono na wydruku 21.2, zaś chat2.cgi — na wydruku 21.3. Pliki z danymi, chatl.dat i chat2.dat, tworzy się samemu — wystarczy je utworzyć, wpisać w nich krótki tekst przykładowy i ustawić im uprawnienia na tyle niskie, aby skrypty CGI mogły je czytać i zapisywać w nich dane. Aby zacząć rozmowę, wystarczy otworzyć w przeglądarce stronę chat.htm. Prezentowana aplikacja używa dwóch plików z danymi i zapisuje w nich dwie ostatnie ' wypowiedzi (użyto oddzielnych plików dla każdej wypowiedzi, aby łatwiejsza była obsługa tych danych, szczególnie z punktu widzenia blokowania). Można zmienić kod ' tak, aby używać więcej niż dwóch plików i w konsekwencji pokazywać więcej wypowiedzi. Określenie częstości odświeżania strony HTML Jedną z rzeczy, które być może warto będzie zmienić w aplikacji, jest pięciosekundowy odstęp między kolejnymi odświeżeniami ekranu. Zmiany dokonuje się w pliku chatl.cgi, a wystarczy po prostu podać żądaną liczbę sekund: "", Czyszczenie odświeżanych elementów graficznych HTML Jest jeszcze jedna kwestia, która dotyczy tym razem pakietu CGI.pm: kiedy użytkownik ' przesyła formularz z elementami graficznymi zawierającymi jakieś dane, a następnie ' skrypt wraca do tegoż formularza, CGI.pm domyślnie kopiuje dane. Załóżmy, że formularz zawiera następujący obszar tekstowy: $co->textarea( -name=>'textarea', -default=>’’, -rows=>4, -columns=>40 ) Jeśli użytkownik w tym obszarze wpisze jakiś tekst i wyśle go do skryptu, skrypt może ten tekst odczytać za pomocą standardowych metod CGI. Jednak przy powrocie na stronę HTML z tym samym formularzem CGI.pm przywraca w obszarze tekstowym pier-i wotny tekst (nawet jeśli jako wartość domyślną tego obszaru podano pusty napis). W aplikacji chat efekt jest taki, że kiedy użytkownik wyśle jakiś tekst, zostanie on przyjęty ale nie zniknie z obszaru, gdzie był wprowadzony. Aby pakiet CGI.pm przywrócił w takim przypadku wartość domyślną, przypisuje się atrybutowi override wartość prawdy: $co->textarea( -name=">' textarea', -default=>'', -override=>l, -rows=>4, -columns=>40 ) Teraz obszar tekstowy jest czyszczony po każdym wysłaniu tekstu, czyli wszystko działa tak, jak powinno. Wydruk 21.1. chat.htm Chat Przeprowadzanie pogawędek wymaga ramek. Wydruk 21.2. chat1.cgi #!usr/bin/perl use CGI; use Fcntl; $co = new CGI; open (DATA1, "; unlockfile(DATA1); close DATA1; open (DATA2, "; unlockfile(DATA2); close DATA2; print $co->header, "", $co->start_html( -title=>'Przykładowy chat’, -author=>'Steve’, -target=> ‘_display', -BGCOLOR=>'white', -LINK=>'red' ), $co->center($co->hl(Pogawędki')), $co->p, $co->p, $co->center($text1), $co->p, $co->center($text2), $co->end_html; exit; sub lockfile { my $count = 0; my $handle = shift; until (flock($handle, 2)) { sleep .10; if(++$count > 50) { print $co->header, "", $co->start_html( -title=>'Przykładowa rozmownica', -author=>'Steve', -target=>'_display', -BGCOLOR=>'white', ), $co->center($co->hl('Serwer jest zajęty’)) $co->end_html; exit; } } } sub unlockfile { my $handle = shift; flock($handle, 8); } Wydruk 21.3. chat2.cgi #!/usr/bin/perl use CGI; use Fcntl; $co = new CGI; if ($co->param() ) { $name = $co->param ( ' username ' ) ; $name =~ s/param( ' textarea' ) ; $text =~ s/; unlockfile (OLDDATA) ; close OLDDATA; open (DATA, ">chatl.dat") or die "Nie można otworzyć pliku z danymi."; lockfile (DATA) ; print DATA $oldtext; unlockfile (DATA) ; close DATA; open (NEWDATA, ">chat2.dat") or die "Nie można otworzyć pliku z danymi."; lockfile (NEWDATA) ; print NEWDATA "", $name, ": ", "", $text; unlockfile (NEWDATA) ; close NEWDATA; } } &printpage; sub printpage { print $co->header, $co->start_html ( -title=> ' Przykładowy chat’, -author=> ' Steve ' , -BGCOLOR=>’white’, -LINK=>'red’ ), $co->startform, $co->center("Proszę podać swoje imię: ", $co->textfield(-name=>'username’), "i wpisać swoje zdanie:"), $co->p, $co->center( $co->textarea( -name=>'textarea', -default=>'', -override=>l, -rows=>4, -columns=>40 ) ) $co->center( $co->submit(-value=>'Wyślij tekst'; $co->reset, ), $co->hidden(-name=>'hiddendata ' ) , $co->endform, $co->end_html; } sub lockfile { my $count = 0; my $handle = shift; until (flock($handle, 2)) { sleep.10; if(++$count > 50) { &printpage; exit; } } } sub unlockfile { my $handle = shift; flock($handle, 8); } Zapisywanie i odczytywanie cookies Ten punkt w całości jest poświęcony zapisywaniu i odczytywaniu cookies, które —jak wiedzą wszyscy użytkownicy Sieci — umożliwiają zapisywanie informacji na komputerze użytkownika. Zanim jednak zaczniesz bezkrytycznie wykorzystywać cookies, zapamiętaj, że opinie o tym mechanizmie są bardzo różne. Użycie cookies Cookies są kochane i nienawidzone. Większość użytkowników ich nie cierpi — widziałem pojedyncze strony zapisujące nawet po 70 ich sztuk (tego rodzaju przesada to więcej niż brak umiarkowania, to już ociera się o samobójstwo, gdyż wiele przeglądarek sieciowych ogranicza liczbę cookies do 200). Jako że cookies umożliwiają programistom śledzenie użytkowników czy realizację sieciowych koszyków na zakupy, programiści kochaj ą cookies. Skrypt pokazany na wydruku 21.4 umożliwia dostosowanie strony tak, aby pozdrawiała gości imiennie, a nawet składała im życzenia urodzinowe, jeśli jest po temu okazja. Skrypt się nie narzuca — jeśli użytkownik nie poda żadnych danych godnych uwiecznienia ani niczego nie zmieni, żadne cookies nie są zapisywane. Skrypt sprawdza dane użytkownika i pilnuje, aby data była podawana w postaci mm/dd (same cyfry, jeden ukośnik w odpowiednim miejscu), a także usuwa wszelkie znaczniki HTML, które mogły zostać użyte w imieniu. Kiedy użytkownik uruchomi skrypt hellocookie.cgi po raz pierwszy, zostanie wyświetlona strona pokazana na rysunku 21.2. Użytkownik może podać na niej swoje imię i datę urodzin w postaci mm/dd. Po kliknięciu przycisku Submit — na komputerze użytkownika jest zapisywane cookie zawierające podane imię i datę. Przy następnym uruchomieniu tego samego skryptu sprawdza się, czy istnieje odpowiednie cookie, a jeśli tak, jest ono odczytywane i wyświetla się powitanie, jak na rysunku 21.3 (ewentualnie dołączane są życzenia urodzinowe). Na tym działanie skryptu się kończy. Jak zapisać cookies? Jeśli korzysta się z pakietu CGI.pm, używanie cookies jest proste. Oto sposób zapisania cookie o nazwie greetings, przechowującego informacje w asocjacji %greegings, przy czym termin ważności cookie wynosi rok: $co = new CGI; $greetingcookie = $co->cookie( -name=>'greetings', -value=>\%greetings, -expires=>'+365d' ) ; print $co->header(-cookie=>$greetingcookie); Aby utworzyć cookies, przekazujemy je jako nazwany parametr metodzie header obiektu CGI. Jak odczytać cookies? W celu odczytania cookies wystarczy użyć metody cookie, przekazując jej nazwę żądanego cookie. Po odczytaniu naszego cookie — greetings — możemy skorzystać z danych z asocjacji %greetings zapisanej w tym cookie: $co = new CGI; %greetings = $co->cookie('greetings '); print $greetings{'name'}; Informacje zawarte w tym punkcie wystarczą do używania cookies. Trzeba jednak być ostrożnym, gdyż wielu użytkowników nie życzy sobie, aby programy działające gdzieś w Sieci zapisywaty na ich komputerach swoje dane. Wydruk 21.4. hellocookie.cgi #!/usr/bin/perl use CGI; $co = new CGI; %greetings = $co->cookie('greetings'); if ($co->param('name')) ($greetings{'name'} = $co->param('name’)} if ($co->param('birthday') =~ m/\d\d\/\d\d/) { $greetings{'birthday'} = $co->param('birthday'); } ($day, $month, $year) = (localtime)[3, 4, 5]; $date = join ("/", $month + l, $day); if (exists ($greetings{ 'name'})) { $greetingstring = "Witaj " . $greetings{'name’}; $greetingstring .= ", wszystkiego najlepszego!" if ($date eq $greetings{ 'birthday '}) $greetingstring =~ s/cookie ( -name=> ' greetings ' , -value=>\%greetings, -expires=> ' +365d' ); if ($co->param('name') || $co->param( 'birthday’)) { print $co->header (-cookie=>$greetingcookie) ; } else { print $co->header; } print $co->start_html ( -title=>"Przykład użycia cookies", ), $co->center ( $co->h1("Przykład użycia cookies"), $co->p, $co->h1($greetingstring) , $prompt, $co->startform, "Twoje imię: ", $co->textfield( -name=> 'name', -default=>' ' , -override=>1 ), $co->p, "Data urodzin (mm/dd) : ", $co->textfield( -name=>'birthday' , -default=>'', -override=>l ), $co->p, $co->submit (-value=>'Submit ' ) , $co->reset, $co->endform, ), $co->end html; Tworzymy grę Książkę tę zakończymy, opisując grę game.cgi, która jest internetową odmianą klasycznej gry słownej. Gra jest interaktywna i dość bezpieczna, gdyż używa się w niej jedynie przycisków radio oraz przycisku Submit i hiperłączy. Gra jest też całkiem sympatyczna, a pokazano jąna rysunku 21.4. Kiedy użytkownik wskaże przeglądarce adres URL skryptu, zostanie wyświetlona strona podobna do strony na rysunku 21.4. Użytkownik może wskazywać litery, korzystając z przycisków radio i przycisku Submit. Jeśli uda mu się odgadnąć słowo przed zrobieniem ośmiu pomyłek, ujrzy stronę z gratulacjami, w przeciwnym razie pokazana mu zostanie strona z prawidłową odpowiedzią i propozycją rewanżu. Aby zacząć nową grę, wystarczy kliknąć dowolną literę i przycisk Submit. Zapisywanie danych na stronie między kolejnymi wywołaniami skryptu Skrypt omawiany w tym punkcie jest dobrym przykładem tego, jak można przechowywać informacje między kolejnymi wywołaniami CGI. Trafienia i pomyłki użytkownika, a także prawidłowa odpowiedź są zapisywane w polach ukrytych formularza. Taki sposób zapamiętywania danych powoduje, iż użytkownik nie musi pamiętać między kolejnymi wywołaniami swoich wcześniejszych posunięć, gdyż sam formularz poda wszystkie potrzebne informacje. Oznacza to również, że użytkownik może oszukać i podpatrzyć odpowiedź, oglądając kod źródłowy strony HTML, ale przecież to tylko gra. Jeśli jednak na samą myśl o ewentualnym oszustwie czujesz niezadowolenie, możesz te informacje zaszyfrować (sugestie, jak to zrobić, znajdziesz w archiwum CPAN pod adresem: www.cpan.org/CPAN.html). Dostosowywanie gry do własnych potrzeb Do uruchomienia gry potrzebny jest skrypt game.cgi z wydruku 21.5. Niezbędny jest też plik answers.dat zawierający słowa, które mogą być odgadywane. W pliku tym może znajdować się dowolna liczba pozycji i słowa te mogą mieć dowolną długość, a należy je umieszczać po jednym w każdym wierszu (zapisywane małymi literami). Warto zauważyć, że nie należy używać przecinków, spacji ani żadnych innych ograniczników. Skrypt przyjmie zwykły plik tekstowy z dowolnymi zakończeniami wierszy: w stylu systemu Unix (\n) lub DOS (\r\n), zatem plik answert.dat można tworzyć w dowolnym systemie i później od razu go umieścić na serwerze. Nie wolno oczywiście zapomnieć o ustawieniu odpowiednich uprawnień do niego, na przykład 644 w systemie Unix — skrypt game.cgi musi być w stanie go odczytać. Oto przykład kilku pozycji z pliku answers.dat: instrukcja historia staranie klawikord kwiatek osoba bielizna Jako że gra zwykle jest kojarzona z jakimiś efektami wizualnymi, skrypt może używać obrazków podobnych do tych z rysunku 21.4 (jeśli jednak tych rysunków nie podasz, to nie ma problemu — skrypt przed użyciem obrazków sprawdza, czy w ogóle istnieją). Poszczególne obrazki umieszcza się w tym samym katalogu, co skrypt game.cgi, i nazywa sieje hangl.gif, hang2.gif i tak dalej, aż do hang8.gif. Kolejne rysunki odpowiadają ekranowi powitalnemu oraz kolejnym możliwym pomyłkom. Obrazek hangl.gif zawierz pustą szubienicę, hangl.gif tę samą szubienicę z głową skazańca — i tak dodaje się kolejne członki, aż do przedostatniej pomyłki, hang8.gif. Jeśli użytkownik i wtedy się pomyli, wyświetlany jest obrazek hang9.gif wraz z prawidłową odpowiedzią. W sytuacji, gdy użytkownik odgadnie hasło, gra kończy się wyświetleniem obrazka hangl0.gif na stronie z gratulacjami. Skrypt można dostosowywać do swoich potrzeb, pozostaje więc życzyć tylko miłej zabawy! Wydruk 21.5. game.cgi #!/usr/bin/perl use CGI; $co = new CGI; i f ($co->param('newgame') eq "yes" l l !$co->param('newgame'))f newgame(); } else ( if($co->param('newgameyesno1) eq "yes")( newgame(); } else { $theanswer = $co->param('answer'); $theguess = getguess(); if($theguess eq "-") { $thehits = $co->param('hits'); Sthemisses = $co->param('misses'] displayresult(); ) else { $thehits = gethitsO; if (index($thehits, "-") eq -1)( youwin () ; } else ( $themisses = getmisses(); if(length($themisses) >= 9)( youlose{);; } else { displayresult() sub newgame $datafile = "answers.dat"; open ANSWERDATA, $datafile; @answers = ; close (ANSWERDATA); srandftime " S$); $indexl = $#answers * rand; $theanswer = $answers[$indexl]; chomp($theanswer); $themisses = "-"; $thehits = ""; for($loopindex = 0; $loopindex < length($theanswer); $loopindex++)( $thehits .= "-"; displayresult (); sub getguess Stheguess = "-"; if ($co->param('letters')){ $theguess = Ic($co->param('letters')); return $theguess; sub displayresult print $co->header, $co->start_html<-title=>'Word Gamę', -author=>'Steve', -bgcolor=>'black', -text=>'#ffff00', -link=>'#ff0000', -alink=>'#ffffff', -vlink=>'#ffff00'), $co->center( "", $co->hl('Word Gamę!'), $co->hr $len = length($themisses); if (-e "hang$(len).gif") ( print $co->img({-src=>"hang$(len}.gif", -align=>left, -vspace=>10, -hspace=>l)); print $co->center( $co->hl($thehits), "", $co->h2("Pomyłki (maks.8): " . substr($themisses, 1)), $co->startform, $co->hidden(-name=>"newgame', -default=>"no", -override=>l), $co->hidden(-name=>'answer', -default=>"$theanswer", -override=>l), $co->hidden(-name=>'hits', -default=>"$thehits", -override=>l) , $co->hidden(-name=>'misses ' , -default=>"$themisses", -override=>l) , $co->br, "Odgadnij literę:", $co->br, ) , "
", "A"; for ($loopindex = ord('B'); $loopindex <= ord('M'); $loopindex++) { $c = chr ($loopindex; print "${c)"; } print $co->br; for ($loopindex = ord('N'); $loopindex <= ord('Z'); $loopindex++) { $c = chr ($loopindex) ; print "${c}"; ) print $co->br, "Następnie odpowiedź prześlij klikając ", $co->submit (-value=> 'ten przycisk')/ $co->br, $co->br, "Gramy jeszcze raz?", " Tak", " Nie", "
", $co->endform, "
", $co->end_html; } sub gethits ( $temphits = $co->param ( 'hits ' ) ; $thehits = ""; for ($loopindex = 0; $loopindex < length ($theanswer) ; $loopindex++) ( $thechar = substr ($temphits, $loopindex, 1); $theanswerchar = substr ($theanswer, $loopindex, 1) if($theguess eq $theanswerchar) ( $thechar = $theguess; } $thehits .= $thechar; } return $thehits; } sub getmisses { $theraisses = $co->param ( 'misses ' ) ; if (index ($theanswer, $theguess) eq -1)( if (index ($themisses, $theguess) eq -1)1 $themisses .= $theguess; return $themisses; sub youwin print ' $co->header, $co->start_html(-title=>'Word Gamę', -author=>'Steve' -bgcolor=>'black', -text=>'#ffff00', -link=>'łff0000' -alink=>'#ffffff', -vlink=>'#ffff00'), "
", "", $co->hl('Word Gamę!'), $co->hr, $co->br, "", ""; if (-e "hangl0.gif") { print $co->img((-src=>"hanglO.gif", -align=>left, -vspace=>10, -hspace=>l)); print $co->hl ("Trafiłeś: ", $theanswer), $co->hl("Wygrałeś!"), $co->br, $co->br, $co->startform, Sco->hidden(-name=>'newgame', -default=>"yes", -override=>l), $co->br, $co->br, $co->submit(-value=>'Nowa gra'), $co->endform, "", "
", $co->end_html; sub youlose print $co->header, $co->start_html(-title=>'Word Gamę', -author=>'Steve' -bgcolor=>'black', -text=>'łffff00', -link=>'IffOOOO', -alink=>'łfff fff', -vlink=>'#ffffOO'), "
", "", $co->hl('Word Gamę!'), $co->hr, $co->br, "", ""; if (-e "hang9.gif") ( print $co->img((-src=>"hang9.gif" , -align=>left, -vspace=>10, -hspace=>l(); print $co->hl("Odpowiedź: ", $theanswer), $co->hl("Przykro mi, zbyt wiele prób!", $co->br, $co->br, "Może następnym razem będzie lepiej."), $co->br, $co->br, $co->startform, $co->hidden(-name=>'newgame', -default=>"yes", -override=>l), $co->br, $co->br, l $co->submit(-value=>'Nowa gra')/ $co->br, $co->endform, "", "
", $co->end htral; Dodatek A Przewodnik Zaleca się używanie przełącznika -w, aby podczas uruchamiania skryptów wyświetlane były ostrzeżenia. Tak samo należy zwrócić uwagę na konieczność użycia w skryptach dyrektywy use strict, aby Perl żądał deklarowania zmiennych i innych symboli. Wartość prawdy — true W Perlu liczba 0 oznacza fałsz, a każda inna wartość to prawda. Wyróżniki przedrostkowe zmiennych Nazwa zmiennej może zawierać litery, cyfry i podkreślenia. Nazwa ta może być długa, przy czym długość ta jest zależna od używanego systemu, ale maksimum to 255 znaków. Nazwa musi się zaczynać od wyróżnika przedrostkowego. Oto wyróżniki Perla i ich znaczenie: * $ zmienna skalarna, * % asocjacja (tablica asocjacyjna), * @ tablica, * & procedura, * * typ ogólny (na przykład *myvar oznacza dowolnego typu myvar, na przykład @myvar, %myvar i tak dalej). Zmienne skalarne Zmienne skalarne zawierają liczby i tekst. W Perlu obsługuje się szereg formatów liczbowych, a zestawiono je w tabeli A. l. Tabela A.l. Zmienne skalarne — liczbowe Typ Przykład Zmiennoprzecinkowe l .23 Szesnastkowe 123 Ósemkowe 0123 Zapis naukowy 1.23E4 Z podkreśleniami 1_234_567 Zamienne skalarne mogą zawierać nie tylko liczby, ale również tekst. W zmiennych skalarnych Perla, zawierających tekst, są obsługiwane znaki specjalne, które zestawiono w tabeli A.2. Listy Perl umożliwia zestawianie wartości skalarnych (a także asocjacji i tablic) w listy. Funkcje wbudowane Perla dzieli się na dwie grupy: obsługujące skalary i obsługujące listy (niektóre funkcje mogą obsługiwać oba rodzaje danych). W Perlu nie istnieje specjalny listowy typ danych, jednak funkcjonuje operator list, czyli para nawiasów, zaś elementy listy w nawiasach rozdziela się przecinkami. Kontekst skalarny a kontekst listowy Dwa podstawowe konteksty Perla to kontekst skalarny i listowy. Kiedy Perl spodziewa się listy, traktuje dane w kontekście listowym, a kiedy spodziewa się skalara, dane traktuje w kontekście skalarnym. Jeśli zatem oczekuje się listy, dane traktuje się jak listę, a jeśli oczekuje się skalara, dane są traktowane jako dane skalarne. Innymi słowy, sposób traktowania danych jest w Perlu określony niejawnie, przez kontekst, w którym danych się używa — nie można tego sposobu określić jawnie, programowo. Tabela A.2. Znaki specjalne Znak specjalny Znaczenie \’ Cudzysłów pojedynczy. \” Cudzysłów podwójny. \t Tabulator. \n Znak nowego wiersza. \u Zamiana następnego znaku na wielką literę. \l Zmiana następnego znaku na małą literę. \U Zmiana wszystkich dalszych znaków na wielkie litery. \L Zmiana wszystkich dalszych znaków na małe litery. \Q Dodanie lewego ukośnika do wszystkich dalszych znaków niealfanumerycznych. \E Kończy działanie \U, \L lub \Q. \r Znak powrotu kursora. \f Otwarcie strony. \b Backspace. \a Sygnał dźwiękowy. \e Escape. \033 Znak zapisany w postaci kodu ósemkowego. \x1b Znak zapisany w postaci kodu szesnastkowego. \c[ Znak kontrolny. Tablice Zmienne tablicowe zaczynają się znakiem @. Tablicę tworzy się, przypisując takiej zmiennej listę: @tablica = (l, 2, 3). Do elementów tablicy można się odwoływać jako do skalarów, poprzedzając odwołania znakiem $, a żądany indeks podaje się w nawiasach kwadratowych. Jeśli tablica nazywa się @tablica, to wyrażenie $#tablica określa indeks ostatniej wartości z tej tablicy. Asocjacje Asocjacje są nazywane także tablicami asocjacyjnymi. Ta ostatnia nazwa dość dobrze opisuje ich konstrukcję: do wskazywania danych — zamiast indeksów numerycznych — używa się w nich klucza, który ma postać napisu, a jest związany z potrzebną wartością. Nazwy asocjacji poprzedza się znakiem %. Jak w przypadku tablic, przy pracy z poszczególnymi elementami asocjacji używa się wyróżnik^ $. Do wartości można odwoływać się przez ich klucz: $wartość = $asocjacja{$klucz} Typy ogólne Typy ogólne Perla zachowują się jak aliasy. Typów tych można używać do wiązania nazwy zmiennej (na przykład dane) z inną nazwą (na przykład takzedane). W ten sposób wszystkie zmienne, w których użyto nowej nazwy, czyli $takzedane, @takzedane, %takzedane i tak dalej, odnoszą się do tych samych danych, co zmienne zawierające pierwszą nazwę, odpowiednio $dane, @dane, %dane i tak dalej. Operatory W tabeli A.3 zestawiono operatory Perla w kolejności malejącego priorytetu. Przypisywanie wartości Operator przypisania, =, przypisuje zmiennej wartość: $zmiennal = 5 Można też użyć skróconego zapisu operatorów przypisania, polegającego na połączeniu przypisania z innym operatorem, na przykład: $dwarazy *= 2 Powyższe przypisanie przemnoży wartość $dwarazy przez 2 i wynik zapisze z powrotem w tej samej zmiennej. Oto komplet dozwolonych operatorów przypisania: = **= += &= <<= &&= -= /= |= >>= ||= .= %= ^= x= Operatory porównania Operatory porównania pozwalają porównać ze sobą dwa argumenty i zwracają wartość zgodnie z tabelą A.4. Tabela A.3. Operatory Perla Operatory Łączność Termy i lewostronne operatory list lewostronna -> lewostronna ++ -- nie dotyczy ** prawostronna ! ~ \ unarny + unarny - prawostronna =~ !~ lewostronna * / % x lewostronna + - . lewostronna << >> lewostronna nazwane operatory unarne, operatory testowania plików nie dotyczy < > <= > lt gt le ge nie dotyczy ++ != <=> eq ne cmp nie dotyczy & lewostronna | ^ lewostronna && lewostronna || lewostronna .. ... nie dotyczy ?: prawostronna = += -= *= prawostronna , => lewostronna prawostronne operatory list nie dotyczy not prawostronna and lewostronna or xor lewostronna Operatory równości Operatory równości porównują dwa argumenty i zwracają wartość zależną od wyniku porównania—zestawiono je w tabeli A.5. Tabela A.4. Operatory porównania Operator Typ danych Zwraca < liczbowe Prawdę, jeśli lewy argument jest mniejszy od prawego. > liczbowe Prawdę, jeśli lewy argument jest większy od prawego. <= liczbowe Prawdę, jeśli lewy argument jest mniejszy lub równy z prawym. >= liczbowe Prawdę, jeśli lewy argument jest większy lub równy z prawym. lt tekstowe Prawdę, jeśli lewy argument jest mniejszy od prawego. gt tekstowe Prawdę, jeśli lewy argument jest większy od prawego. le tekstowe Prawdę, jeśli lewy argument jest mniejszy lub równy z prawym ge tekstowe Prawdę, jeśli lewy argument jest większy lub równy z prawym. Tabela A.5. Operatory równości Operator Typ danych Zwraca == liczbowe Prawdę, jeśli lewy argument jest równy prawemu argumentowi. != liczbowe Prawdę, jeśli lewy argument nie jest równy prawemu argumentowi. <=> liczbowe -1, 0 lub 1 w zależności od tego, czy lewy argument jest numerycznie odpowiednio mniejszy, równy bądź większy od prawego argumentu. eq tekstowe Prawdę, jeśli lewy argument jest równy prawemu argumentowi. ne tekstowe Prawdę, jeśli lewy argument nie jest równy prawemu argumentowi. cmp tekstowe -1, 0 lub 1 w zależności od tego, czy lewy argument jest odpowiednio mniejszy, równy bądź większy Instrukcja if Instrukcja i f sprawdza, czy podany w nawiasach warunek jest spełniony. Jeśli wyrażenie w nawiasach zwraca wartość prawdy (czyli nie 0), wykonywany jest blok kodu instrukcji if. Istnieje też możliwość wykorzystania frazy else zawierającej kod wykonywany w przypadku niespełnienia warunku, można w końcu zastosować frazę elsif (nie mylić z else if czy elseif), która pozwala zrealizować dodatkowe kontrole. Oto składnia instrukcji i f: if (WYRAŻENIE) BLOK if (WYRAŻENIE) BLOK else BLOK if (WYRAŻENIE) BLOK elsif (WYRAŻENIE) BLOK... else BLOK Instrukcja unless Instrukcja unless działa wprost przeciwnie niż instrukcja if — blok kodu jest wykonywany wtedy, gdy podany warunek nie zostanie spełniony. Oto składnia unless: unless (WYRAŻENIE) BLOK unless (WYRAŻENIE) BLOK else BLOK unless (WYRAŻENIE) BLOK elsif (WYRAŻENIE) BLOK... else BLOK Instrukcja for Pętli for używa się do wielokrotnego wykonywania instrukcji, zwykle stosuje się przy tym indeks pętli. Oto konstrukcja pętli for: ETYKIETA for (WYRAŻENIE; WYRAŻENIE; WYRAŻENIE) BLOK Pierwsze wyrażenie jest wykonywane przed wykonaniem bloku, drugie natomiast jest sprawdzane przed każdą iteracj ą pętli i jeśli jego wartością jest fałsz, pętla jest kończona (w związku z tym blok może nie wykonać się ani razu). Trzecie wyrażenie jest wykonywane po każdej iteracji. Instrukcja foreach Pętla ta jest właściwie tą samą pętlą co for, ale programiści często używają jej do iteracji po elementach listy. Oto obowiązująca składnia: ETYKIETA foreach ZMIENNA (LISTA) BLOK Instrukcja while Pętla wykonuje blok tak długo, jak długo WYRAŻENIE jest prawdziwe: ETYKIETA while (WYRAŻENIE) BLOK ETYKIETA while (WYRAŻENIE) BLOK continue BLOK Użycie pętli until Pętla wykonuje blok tak długo, jak długo wyrażenie ma wartość fałszu (czyli odwrotnie niż while): ETYKIETA until (WYRAŻENIE) BLOK ETYKIETA until (WYRAŻENIE) BLOK continue BLOK Modyfikowanie instrukcji za pomocą fraz if, unless, until i while W Perlu niekoniecznie trzeba używać formalnych instrukcji warunkowych i instrukcji pętli, można też zastosować modyfikatory umieszczane za zwykłymi instrukcjami: if WYRAŻENIE unless WYRAŻENIE while WYRAŻENIE until WYRAŻENIE Instrukcje pętli Oto instrukcje dostępne wewnątrz pętli: * Polecenie next loop powoduje natychmiastowe rozpoczęcie następnej iteracji pętli, z pominięciem wszelkich instrukcji znajdujących się za tym poleceniem; * Polecenie las t kończy natychmiast wykonywanie bieżącej pętli; * Polecenie redo powoduje ponowne wykonanie bieżącej iteracji pętli bez powtórnej ewaluacji warunku pętli. Instrukcja goto Perl zawiera instrukcję goto, ale jej stosowanie może zaowocować dzikimi skokami, które bardzo trudno będzie śledzić, gdyż wykonanie zostaje przeniesione w całkiem inne miejsce. Oto dopuszczalne postaci goto: goto ETYKIETA goto WYRAŻENIE goto &NAZWA Pierwsza postać powoduje przeniesienie punktu wykonywania do instrukcji oznaczonej etykietą. W drugiej postaci zakłada się, że WYRAŻENIE da w wyniku etykietę, do której ma nastąpić skok. Natomiast ostatniej postaci używa się w połączeniu z procedurami. Procedury W Perlu deklaracji procedury używa się po to, aby poinformować interpreter o istnieniu takiej procedury, o typie jej parametrów i rodzaju wartości zwracanych. Trzeba odróżnić deklarację procedury od jej definicji. Definiowanie procedury polega na podaniu kodu tworzącego jej treść (ciało). W Perlu — w przeciwieństwie do innych języków programowania — deklarowanie procedur nie jest obowiązkowe, chyba że procedury te mają być wywoływane bez podawania ich parametrów w nawiasach (dotyczy to zatem na przykład operatorów list): w takim przypadku deklaracja lub definicja przed użyciem procedury jest obowiązkowa. Oto sposób deklarowania procedur: sub PROCEDURA; sub PROCEDURA (PROTOTYP) ; sub PROCEDURA BLOK sub PROCEDURA (PROTOTYP) BLOK W celu zadeklarowania prototypu podaje się we właściwej kolejności znaki, od których muszą się zaczynać poszczególne parametry: $ oznacza skalar, @ oznacza tablicę i tak dalej. Kilka przykładów pokazano w tabeli A.6. Kod procedury podaje się w jej definicji, a samą definicję wyróżnia się, używając słowa kluczowego sub: sub NAZWA BLOK sub NAZWA (PROTOTYP) BLOK Tabela A.6. Przykłady deklaracji procedur Perla Deklaracja Sposób wywołania sub NAZWA($) NAZWA $parametrl; sub NAZWA($$) NAZWA $parametrl, $parametr2; sub NAZWA($$;$) NAZWA $parametrl, $parametr2, $parametropcj onalny; sub NAZWA(@) NAZWA $tablical, $tablica2, $tablica3; sub NAZWA($@) NAZWA $parametrl, $tablical, $tablica2; sub NAZWA(\@) NAZWA @parametrl; sub NAZWA(\%) NAZWA %{$wskaźnik_asocjacji}; sub NAZWA(&) NAZWA proceduraanonimowa; sub NAZWA(*) NAZWA *parametrl; sub NAZWA() NAZWA; Odczyt parametrów przekazanych procedurze Parametry przekazane procedurze mogą zostać odczytane z tablicy @_, która została utworzona właśnie w celu ich przechowywania. Jeśli na przykład zostaną przekazane dwa parametry, w kodzie procedury można się do nich odwoływać przez wyrażenia $[0] i $[l]. Zwracanie wartości z procedur Wartość zwracana z procedury to wartość ostatniego wyliczonego wyrażenia. Do zakończenia działania procedury można też użyć instrukcji return, podając jej wartość zwrotną. Wartość ta jest przetwarzana stosownie do kontekstu, w którym procedura została wywołana. Wskaźniki Wskaźniki, które tworzy się za pomocą lewego ukośnika \, nazywamy wskaźnikami bezpośrednimi. Taki wskaźnik zawiera adres wskazywanych danych oraz ich typ. Wskaźnik symboliczny zawiera nazwę danych, a nie bezpośrednie wskazanie na dane. Dereferencja wskaźników Do dereferencji wskaźnika używa się operatora $ — dereferencja to określenie wartości wskazywanej przez wskaźnik. W przypadku używania tablic, asocjacji i procedur można ułatwić realizację dereferencji, stosując operator strzałki. Zmienne wbudowane Poniżej w tabeli A.7 zestawiono wbudowane zmienne Perla. Tabela A.7. Wbudowane zmienne Perla Zmienna wbudowana Zawartość Zmienna wbudowana Zawartość $' tekst za wzorcem $- numery wierszy w lewej części strony $! ostatni błąd $" separator pól wyjściowych $# format wyjściowy liczb $$ numer procesu Perla $% numer strony wejściowej $& ostatnie dopasowanie $( rzeczywisty GID $) obowiązujący GDI $* wzorce wielowierszowe $, separator pól wejściowych $. numer wiersza wejściowego $/ separator rekordów wejściowych $: znaki dzielenia wiersza $; separator indeksów $? status ostatniego zamknięcia potoku, wywołania polecenia przez` lub wywołania funkcji systemowej $[ początek indeksów tablicy $\ separator rekordów wyjściowych $] wersja Perla $^ format nagłówka strony $^A zapis bufora $^D flagi debugowania $^E informacje o błędzie związane z systemem operacyjnym $^F największy systemowy deksryptor pliku $@ błąd ostatniej funkcji eval $^S stan interpretera $^H ustawienia kontroli składniowej $^I wartość edycji w miejscu $^L zmiana strony wyjściowej $^M awaryjny bufor pamięci $^O nazwa systemu operacyjnego $^P obsługa debugowania $^R wynik asercji ostatniego wyrażenia regularnego $^T czas uruchomienia skryptu $~W ustawienie przełącznika ostrzeżeń $^X nazwa pliku interpretera $_ zmienna domyślna $` tekst przed wzorcem $| wymiatanie bufora wyjściowego $~ nazwa formatu raportowania $+ ostatnie dopasowanie nawiasów $< rzeczywisty UID $= obowiązująca długość strony $> obowiązujący UID $0 nazwa programu $ARGV nazwa bieżącego pliku $n dopasowanie numer n %ENV ustawienia środowiska %INC pliki uruchamiane %SIG obsługa sygnałów @_ parametry procedur @ARGV parametry wiersza poleceń @INC położenie uruchamianych skryptów Pakiety Aby pakiet utworzyć lub zmienić pakiet bieżący, używa się instrukcji package: package package PRZESTRZEŃ_NAZW Konstruktor pakietu nazywa się BEGIN, a jego destruktor to end. Do eksportowania symboli z pakietu używa się Exportera — modułu Perla. 1 Wyjątkowo jest też traktowany znak podkreślenia, jeśli wystąpi zaraz po znaku $, przed jakimil wiek literami. Wtedy znak podkreślenia również musi być jedynym znakiem w nazwie zmiennej. Czarna Księga - PERL 2