Wyjściowka nr. 7 :S
Uniwersytet Gdański - Instytut Matematyki - Zakład Informatyki - Strona domowaPytania i zadania
Odpowiedz na zebrane poniżej zadania.
Odpowiedzi wyślij mailem pod tyt. [kolo 7] grupaN Imię Nazwisko (N-numer grupy)
Przestrzegaj limitu czasu pisania - max 15 minut. Max punktów do zdobycia: 7. Powodzenia!
Zadania dla grupy 1
Start: 2007-04-25 15:05, czas: 15 minut czasu delty.- (1p) Czy w perlu istnieje słowo kluczowe definiujące klasę, podobnie jak Javie, C++ lub w PHP? Jeżeli tak, to napisz które.
Tak, i nie :) Nie ma słowa dedykowane wyłącznie do tworzenia klas, ale można użyc słowa package, aby zasymulować konstrukcję klasy. I tak właśnie się robi. Ewentualnie można skorzystać z filtrów kodu źródłowego i stworzyć sobie coś, co rzeczywiście pozwoli budować obiekty za pomocą słowa kluczowego class (lub dowolnego innego), ale to jest temat, który nie był omawiany.
- (1p) Podaj przykład dowolnego znanego Ci obiektowego modułu perla i pokaż przykład tworzenia obiektów przy jego wykorzystaniu.
Np. moduł IO::File, Benchmark, lub Data::Dumper. Przykłady tworzenia obiektów są opisane nawet na stronie, ale dla ścisłości:
use IO::File; use Benchmark; use Data::Dumper; my $io_file = IO::File->new(); my $bench = new Benchmark; my @tab = (); my $dump = Data::Dumper->new(\@tab);
- (1p) Co powoduje, że dana referencja staje się referencją do obiektu? Podaj perlowy przykład.
Przemianowanie referencji na referencję do obiektu wykonuje operacja bless. Za pomocą bless, dowolną referencję można 'pobłogosławić' jakimkolwiek napisem, i wykorzystuje się tę technikę do tworzenia obiektów, czyli referencji pamiętających z jakiej klasy pochodzą. Oto prosty przykład mianowania ('błogosławienia') referencji, tworzący z niej pseudoobiekt:
my $a = 1; my $b = \$a; bless $b,'obiekt';
Po operacji bless, referencja $b jest obiektem, ściśle mówiąc - instancją klasy obiekt. Nie ma znaczenia, czy taka klasa istnieje, czy nie - referencja w żaden sposób tego nie sprawdza. Oczywiście wywołania metod obiektowych w postaci $b->metoda(); nie muszą działać, ale dla perla zmienna $b jest już nie tylko referencją, jest także już obiektem. - (1p) Napisz prosty konstruktor klasy Kolo, nie posiadającej żadnych atrybutów ani elementów składowych, który nie obsługuje dziedziczenia, wykorzystujący referencję do anonimowego hasza.
Oto kod konstruktora, wpisany w klasę Kolo:
package Kolo; use strict; sub new { bless {},'Kolo'; }
Jest to najprostsza i najbardziej prymitywna forma konstruktora, ale wystarcza w zupełności jako odpowiedź na zadanie. Niestety, wielu studentów nie stanęło na 'niskości' zadania i pisało bezmyślnie znacznie bardziej rozbudowane wersje. - (2p) Czym różnią się wywołania metod alfa z klasy Baza, zrealizowane tak:
Baza::alfa();
Baza->alfa();
Pierwsze wywołanie działa tak, jak wywołanie zwykłej funkcji o nazwie kwalifikowanej pakietowo. Oznacza to, że funkcja zostanie wywołana po prostu jako funkcja z przestrzeni nazw Baza i nie zostaną jej przekazane żadne dodatkowe argumenty. Ten styl wywołania stosuje się do zwykłych funkcji w przypadku modułów nieobiektowych. Drugie wywołanie jest uproszczoną wersją wywołania metody alfa na sposób obiektowy z klasy Baza. Pełna postać takiego zapisu powinna wyglądać tak: Baza::->alfa(); (tylko taka postać jest zawsze w pełni jednoznaczna dla kompilatora). Wywołanie metody jest tutaj wywołaniem metody klasy, a nie egzemplarza. Jednocześnie, mimo braku listy parametrów formalnych przekazanych do metody, niejawnie zostanie przekazana nazwa klasy, a więc metoda otrzyma jeden argument tekstowy o treści "Baza". - (1p) Zadanie dowolne, dotyczące obiektów perla BEZ dziedziczenia. Jedynie proste klasy i obiekty :)
Zadania dla grupy 2
Start: 2007-04-26 16:45, czas: 15 minut czasu delty.- (1p) Co to jest instancja klasy?
Instancja, inaczej egzemplarz klasy jest to zmienna obiektowa posiadająca swój własny stan wewnętrzny, i stanowiąca przedstawiciela klasy 'spersonalizowanego'. Instancje danej klasy mogą różnić się między sobą posiadanymi informacjami i mogą ulegać zmianom.
- (1p) Jakie są zalecenia dotyczące eksportowania symboli z modułu dla modułów obiektowych? Ściślej, czy zaleca się eksportować metody, czy raczej nakazuje się tego unikać?
W przypadku modułów, które mają pracować jako moduły obiektowe, czyli klasy, zaleca się unikać eksportowania symboli. Niemniej są od tego zalecenia wyjątki. Eksport może być wskazany także dla klas singletonowych, które pozwalają utworzyć tylko jedną instancję danej klasy. Eksport w pozostałych przypadkach ogólnie nie jest zalecany, przynajmniej nie bezwarunkowo, z tego względu, że zbyt nachalne eksportowanie symboli może 'zanieczyścić' przestrzeń nazw pakietu importujacego, co rzadko jest wskazane. Warto natomiast stworzyć interfejs modułu do eksportu na żądanie, w celu udostępnienia metod i symboli, które ktoś jawnie wymieni w liście importu podczas ładowania modułu (w pragmie use). Przykłady:
use Text::Wrap(); # nie importuje niczego use Text::Wrap qw(wrap); # importuje nazwę funkcji wrap use MyModule qw(:all); # importuje jawnie wszystkie udostępnione przez programistę symbole
- (1p) Wyjaśnij do czego służy polecenie bless.
Polecenie bless to dość specyficzna komenda pozwalająca 'otagować', lub też 'oznaczyć' dowolną referencję dodatkowym napisem, który często jest nazwą klasy. W wyniku działania bless referencja perla pozostaje referencją, ale przenosi też specyficzny napis wraz ze sobą. Typowe zastosowania to tworzenie różnego rodzaju obiektów. Nietypowe, to np. dodawanie opisów do zmiennych, tagowanie lub podpisywanie. Operator bless wymaga jednego parametru, opcjonalnie dwóch. Pierwszy z nich zawsze musi być rerefencją (daną nawet w postaci wyrażenia), drugi parametr to napis. Jeżeli drugi parametr nie jest podany, używana zamiast niego jest nazwa bieżącego pakietu. Przykłady:
bless $a; # oznakowanie zmiennej $a zawierajacej referencje nazwa bieżącego pakietu bless $a,__PACKAGE__; # dokladnie to samo (nazwa __PACKAGE__ to nazwa bieżącego pakietu) bless \@T,'pio2005'; # 'podpisanie' referencji do tablicy @T bless {},$napis; # zwiazanie referencji do anonimowego hasza danego jako wyrazenie i napis z $napis
- (1p) Stwórz obiekt klasy o nazwie takiej jak twoje imię, nie używając konstruktora.
Mam na imię ... nieważne, załóżmy że Zyndarm :) Zatem aby stworzyć obiekt klasy Zyndarm, bez konstruktora, lecz brutalnie w kodzie, trzeba zrobić tak:
$a = { }; # to bedzie nasz przyszly obiekt - rezerwujemy referencje do hasza anonimowego bless $a,'Zyndarm'; # i gotowe!
- (2p) Wskaż które i wyjaśnij dlaczego, wywołanie konstruktora jest najbardziej poprawne:
my $obj = new Object();
my $obj = Object::->new();
my $obj = Object->new();
Najbardziej poprawna jest wersja środkowa, ponieważ jest w pełni jednoznaczna dla perla. Pierwsze i ostatnie wywołanie metody klasy o nazwie new jest zawodne, ponieważ w nietypowych sytuacjach wystąpi konflikt nazw i wywołanie nie nastąpi z klasy Object lub nie zostanie wykonana TA procedura new. Takie sytuacje nietypowe wystąpią wtedy, gdy perl bedzie musiał decydować co wywołać, gdy np. w bieżącym pakiecie będą zdefiniowane funkcje o nazwie Object lub new. Ich wywołanie będzie wtedy bardziej stosowne od wołania dalekiego metody obiektowej na klasie Object. Mimo, że my sami niekoniecznie musimy takie funkcje definiować, może się zdarzyć, że przy bardziej oryginalnej kombinacji nazw oraz jakimś nieprzewidzianym imporcie symboli do przestrzeni nazw bieżącego pakietu perl uzna, że chcemy zrobić coś innego, niż zamierzamy :) - (1p) Zadanie dowolne, dotyczące obiektów perla BEZ dziedziczenia. Jedynie proste klasy i obiekty :)
Zadania dla grupy 3
Start: 2007-05-11 13:15, czas: 15 minut czasu delty.- (1p) Czym różni się wywołanie metody klasy od wywołania metody instancji? Podaj przykłady.
Wywołanie metody klasy w perlu to sposób wywołania obiektowego, gdy zamiast właściwego obiektu użyta zostaje klasa, która dane obiekty definiuje. Przypomina to wywołanie statyczne metody, i opiera się na podobnych zasadach:
Klasa->metoda();
Takie wywołanie ma swoje skutki: wewnątrz metody nie można używać żadnych dynamicznych właściwości obiektu, a jedynie statycznie zapamiętane właściwości klasy. Ponadto w takim wywołaniu nie istnieje w ogóle żaden obiekt z którym można pracować. Pisanie metod wywoływanych wyłącznie jako metody klasy wymaga pewnej uwagi i doświadczenia. Tak pisane i wywoływane są zwykle tylko konstruktory. W przypadku gdy metoda wołana jest jako metoda instancji, klasa musi posiadać utworzony w pamięci swój egzemplarz, będący właściwym obiektem, ze swoim stanem i właściwościami. Wykonanie metody instancji następuje jakby 'wprost' z posiadanego obiektu, i jest dla obiektu najbardziej właściwym sposobem wywołania metod:$instancja->metoda();
Wewnątrz tak wywołanej metody mamy dostęp do danych instancji, czyli posiadamy informacje o właściwościach danego obiektu. Mamy także wszystkie dane dostępne w klasie, podobnie jak w wywołaniu w formie metody klasy. - (1p) Każdy obiekt perla w pewien sposób przechowuje swój wewnętrzny stan, swoje dane, zwane polami lub elementami składowymi. Jaka jest najprostsza implementacja struktury takiego obiektu? Wyjaśnij, jakiej struktury danych naprościej użyć i dlaczego.
Najprostszym obiektem perla może być referencja do skalara. Zależnie od zastosowania, sama wartość tego skalara może być używana lub nie. Obiekt taki nie może jednak pamiętać niczego szczególnego, ponieważ jest tylko prostym skalarem. Dlatego zamiast referencji do skalara stosuje się zależnie od potrzeb referencje do tablicy lub do hasza, które mogą przechować cały szereg różnych danych. W przypadku referencji do tablicy, kolejne pola obiektu dostępne są przez indeksy tej tablicy, a w przypadku hasza można dostać się do pól za pomocą kluczy tego hasza. Budowa obiektu oparta o hasz jest najbardziej chyba uniwersalna i najłatwiejsza do zrozumienia, zwłaszcza, gdy zna się już programowanie obiektowe w innych językach. Hasz stanowi wtedy jakby model, symulację struktury z polami, które można ustawiać jako elementy składowe obiektu. Prawdziwym 'zakręconym' modelem obiektów jest użycie zamiast referencji do danych, referencji do kodu. Obiekt może wtedy dynamicznie zmieniać swoją wewnętrzną strukturę i to bez użycia metod (kod inside-inside). To tak, jakby nagle pod normalną warstwą rzeczywistości umieścić drugą warstwę :)
- (1p) Czym może być drugi argument wywołania funkcji bless? Podaj przykłady.
Drugi argument wywołania funkcji bless może być tekstem lub wyrażeniem zwracającym tekst. W przypadku gdy argument zostanie ten pominięty, zostanie w jego miejsce wstawiona nazwa bieżącego pakietu. W ogólności, argumentem może być dowolne wyrażenie. Jego wynik będzie wtedy przez perla konwertowany do odpowiedniego typu danych (przynajmniej nastąpi taka próba) :) Przykłady:
bless { },'Napis'; bless [ ],$zmienna_z_napisem; bless { },ref $blogoslawiona_referencja; bless { }; bless $a,ref $a; # nic nie zmienia, jezeli $a jest obiektem
- (1p) W jaki sposób odróżnić wywołanie metody klasy od wywołania metody instancji?
Należy sprawdzić, jakiego rodzaju jest pierwszy argument przekazany do metody. Jeżeli jest to tekst, mamy do czynienia z wywołaniem metody klasy, jeżeli jest to referencja do jakiegoś obiektu, wywołanie jest wywołaniem metody instancji. Nie ma jednak całkowitej pewności co do rzeczywistego wywołania, ponieważ można oba wywołania zasymulować w metodzie obiektowej, która jest przecież zwykłą funkcją perla, w sposób następujący:
Class->metoda(); # symulacja: Class::metoda('Class'); $Object->metoda(); # symulacja: Class::metoda($Object);
- (2p) Napisz konstruktor kopiujący nie obsługujący dziedziczenia, działający w klasie zbudowanej z jednowymiarowego hasza (czyli nie zawierającego wielopoziomowych odwołań).
Konstruktor kopiujący to taki konstruktor, który jest wołany jako metoda instancji i jego zadaniem jest nie tylko stworzenie i zainicjalizowanie nowego obiektu, ale też ustawienie wszystkich jego pól w taki sam sposób, jak ma ustawione obiekt wywołujący. Oto prosty kod, który realizuje takie zadanie (oczywiście przy założeniu, że chodzi o kopiowanie jedynie jednopoziomowego hasza):
sub copy { my $class = shift; my $self = ref $class ? { %$class } : {}; bless $self, ref $class || $class; }
Hardcorowi i zdesperowani programiści perla mogą napisać także kod nieco alternatywny:sub hardcopy { bless (ref $_[0] ? {%{$_[0]}} : {}), ref $_[0] || $_[0]; }
i tyle... :) - (1p) Zadanie dowolne, dotyczące obiektów perla BEZ dziedziczenia. Jedynie proste klasy i obiekty :)
Zadania dla grupy 4
Start: 2007-05-11 14:15, czas: 35 minut czasu delty.- (1p) W jaki sposób wywołać metodę obiektową tak, aby nie została ona wywołana jak metoda obiektowa, ale jak zwykła procedura perla?
Należy zastosować wywołanie z kwalifikacją pakietową (gdyż zwykle metody obiektowe nie są importowanie do przestrzeni nazw bieżącego pakietu), w taki oto sposób:
Pakiet::metoda(...);
Po takim wywołaniu perl w ogóle nie zorientuje się, że jest to metoda obiektowa, i wywoła ją jak zwykłą. Jeżeli jednak była to metoda obiektowa, która odbiera pierwszy argument i spodziewa się, że będzie on obiektem, może wystąpić błąd. - (1p) Czy fragmentu kodu przedstawionego poniżej można użyć do kopiowania obiektów?
use Klasa; my $klasa = Klasa->new(); my $class = $klasa;
Nie można. Skopiowana zostanie jedynie referencja, i po takiej operacji obie zmienne skalarne będą wskazywały na ten sam obiekt. Zatem zmiana jego wnętrza dokonana przez jedną z tych zmiennych będzie widoczna również poprzez drugą zmienną, patrz przykład:$klasa->{bla} = 1; print $class->{bla};
Aby nie tylko referencje wskazujące na obiekt, ale także wnętrze obiektu zostało skopiowane, należy wykonać prawdziwą operację kopiowania, element po elemencie. Nikt tego za nas nie zrobi (nie ma też magicznej funkcji memcpy znanej z C, która radośnie, bez kontroli zakresów, prosto i bezpośrednio kopiowała fragment pamięci z jednego miejsca w drugie miejsce). - (1p) Polecenie bless wymaga jednego lub dwóch argumentów. Czym może być pierwszy argument tego polecenia?
Pierwszy argument polecenia bless musi w ostatecznym rachunku okazać się referencją. Natomiast nie musi to być zmienna, może to być natomiast wyrażenie lub literał stały, np.:
bless $a; # zmienna bless {}; # literał stały - hasz anonimowy bless funkcja(); # funkcja musi zwracać referencję bless [@tablica]; # anonimowa tablica utworzona ze zwykłej tablicy (prościej jest \@tablica)
- (1p) W jaki sposób w klasie perla przechować dane statyczne, nie należące do danych instancji?
Są na to przynajmniej trzy sposoby, zależnie od tego, jak bardzo chcemy ukryć zmienne lub dane. Pierwszy sposób, to zdeklarowanie w klasie zmiennej pakietowej, np.:
package Klasa; our $zmienna = 1;
Dostęp do takiej zmiennej będą miały wszystkie metody każdej instancji, ale sama zmienna nie będzie należała do żadnej z nich. Zmiana tej zmiennej będzie widoczna od razu we wszystkich instancjach. Dostęp jest możliwy z dowolnego miejsca przez zapis $Klasa::zmienna a we wnętrzu klasy przez zapis $zmienna. Zmienną taką można zablokować przed zmianami za pomocą pragmy use constant zmienna=>1; lub modułu Readonly, np. Readonly $zmienna => 1;. Wtedy otrzymamy wartość niemodyfikowalną. Drugi sposób to utworzenie w klasie Klasa zmiennej leksykalnej, do której dostęp spoza klasy będzie niemożliwy, natomiast wewnątrz klasy można będzie odwołać się do niej przez zapis $zmienna. Oczywiście można taką zmienną zdefiniować przez my $zmienna = 1;. Zrobienie tego w połowie kodu (po kilku definicjach funkcji, a przed kilkoma następnymi) spowoduje, że zmienna ta będzie widoczna tylko dla funkcji zdefiniowanych później. Można w ten sposób ograniczyć widoczność zmiennej w obrębie danej klasy jedynie do pewnych funkcji. Ostatni sposób jest prawdziwie paranoiczny: rozwiązanie ze zmienną leksykalną stosujemy w obrębie anonimowego bloku, powodując prawdziwą lokalność względem samego modułu Klasa. Jedynie funkcje/metody zdefiniowane w tym samym bloku będą miały dostęp do tej zmiennej, i żadne inne. Przypomina to trochę techniki domknięcia, ponieważ anonimowy blok z zadeklarowaną z nim zmienną leksykalną tworzy niewielkie środowisko, w którym zmienne mogą posiadać swój stan i przesłaniać zmienne globalne, zadeklarowane na poziomie bardziej globalnym. Oprócz tych trzech sposóbów, istnieją inne, ale są bardziej skomplikowane lub naciągane, np. stosowanie bloków __DATA__, używanie danych zewnętrznych pochodzących z baz danych (DBI, DB_File), plików (Storable, LibXML) lub procesów (IPC, SOAP), itp. - (2p) W jaki sposób powinny być napisane metody obiektowe, aby dało się wykonywać je w postaci łańcucha:
$obj->metoda1->metoda2->metoda3()->metoda4;
Podaj przykład najważniejszego fragmentu kodu.Wystarczy zapewnić, że każda metoda zwraca jako swój rezultat referencję do obiektu którego instancja tę metodę wywołała, co robi się umieszczając obiekt w ostatnim wierszu każdej metody:sub metoda1 { my $self = shift; ... $self; } sub metoda2 { my $self = shift; ... $self; } ...
Powoduje to, że wynik działania każdej z metod jest obiektem, z którego można od razu wywołać kolejną metodę. Ten sposób tworzenia metod jest bardzo popularny i wygodny do tworzenia całych łańcuchów wywołań. - (1p) Zadanie dowolne, dotyczące obiektów perla BEZ dziedziczenia. Jedynie proste klasy i obiekty :)
- (1p) W jaki sposób dołączyć pakiet X zdefiniowany w tym samym pliku do programu głównego, który jest wpisany w pakiecie main na końcu tego pliku?
Chodzi o przypadek kodu gdzie na początku jest jakiś pakiet, a potem pakiet main zawierający program główny:
package X; ... package main; ... program główny __END__
Nie trzeba wtedy dołączać pakietu X, ponieważ jest już dołączony. Nie umieszcza się zatem w pakiecie main żadnych dodatkowych poleceń, w tym nie należy pisać use X;. - (1p) Wyjaśnij, w jaki sposób w perlu można wykonać krzyżowe ładowanie modułów, w których funkcje odwołują się do siebie nawzajem. Podaj przykład.
Robi się to za pomocą pragmy use oraz automatycznego eksportowania symboli do pakietu wywołującego. Szczegółowy opis znajduje się w zadaniu 2 dla grupy 4 z koła nr. 6, dostępny tutaj. Opisano tam moduł A oraz moduł C, które zawierają implementacje funkcji a oraz c, wywołujących się krzyżowo. Znajdziesz tam wyjaśnienie oraz przykład kodu perla.
- (1p) Czy można zdefiniować własną funkcję sub? Wyjaśnij dlaczego.
Można, dlatego, że definicja ta nie narusza niczego. Każda funkcja sub należy do bieżącego pakietu, np. main i jej nazwa w pełni kwalifikowana jest zawsze poprzedzona nazwą pakietu. Tak więc tworząc funkcję sub w pakiecie main tworzymy funkcję &main::sub. Natomiast samo sub jest słowem kluczowym i działa niezależnie od naszej definicji. Perl sam "wie", kiedy mowa o prawdziwym sub, a kiedy wołamy sub które jest naszą funkcją.
- (3p) Za pomocą debuggera perlowego znajdź przynajmniej jeden błąd w programie przedstawionym poniżej i podaj polecenia, których użyłeś. Wyjaśnij też na czym polega błąd (lub błędy!). Wskazówka: program możesz zmienić, aby lepiej wykorzystać debugger.
#!/usr/bin/perl -w use strict; sub silnia { return 1 if $_=shift() < 2; return (+$_) * silnia($_-1); } print silnia(5);
Wyjaśnienie tego zadania, razem z logiem z debuggera zostało już opisane w rozwiązaniach do kolokwium nr 6, w grupie 4, zadanie 4, dostępne tutaj. - (1p) Zadanie dowolne.
Jeżeli na tej stronie znalazłeś błąd, lub znasz lepsze wyjaśnienia zadań, proszę, napisz do mnie na adres manty.
Uniwersytet Gdański - Instytut Informatyki - Strona domowa - Perl - Kolokwia[c] Piotr Arłukowicz, materiały z tej strony udostępnione są na licencji GNU.