Egzamin połówkowy :(

Uniwersytet Gdański - Instytut Matematyki - Zakład Informatyki - Strona domowa

Rozwiązania zadań egzaminacyjnych

Zadania okazały się dość trudne, i takie też miały być. Poniżej umieszczam propozycje rozwiązań z wyjaśnieniem dlaczego ma być tak, a nie inaczej. W niektórych przypadkach możliwe są także inne interpretacje, ale wtedy napisz do mnie o tym.

Do wykonania było 20 zadań testowych wielokrotnego wyboru. Zaznaczenie złej odpowiedzi powodowało utratę pytania (nawet jeżeli zaznaczono też dobrze wszystkie prawidłowe odpowiedzi). Pominięcie dobrej odpowiedzi nie miało konsekwencji. Akceptowane były zatem tylko pytania z samymi dobrymi odpowiedziami, niekoniecznie ze wszystkimi.

  1. Które polecenie wypisze plik tekstowy nazwiska.txt bez żadnej modyfikacji?

    a) perl -n -e 'print' nazwiska.txt

    b) perl -pe1 nazwiska.txt

  2. c) perl -e 'print `cat nazwiska.txt`'

    d) perl -e -n 'print $_' nazwiska.txt

    Prawidłowe odpowiedzi to [a], [b], i [c]. Nieprawidłowa odpowiedź to [d].
    [a] W odpowiedzi [a] polecenie działa w typowej kombinacji opcji -ne, która nakazuje perlowi wykonywać skrypt podany po -e dla każdego wczytanego wiersza. Wiersze wczytywane są z pliku, którego nazwę podano jako argument programu. Sam program jest krótki - jego cała treść to print, dlatego, że resztę dodaje opcja -n. Ten program w rzeczywistości wygląda tak: while(<STDIN>){ print $_; } Pętla while wraz z poleceniem czytającym ze standardowego wejścia (<STDIN>) tutaj zastępowana jest czytaniem wprost z pliku. Wczytany wiersz umieszczany jest w zmiennej domyślnej ($_), ponieważ żadnej innej nie podano.
    [b] Tutaj program jest jeszcze krótszy, a to dlatego, że użyto przełącznika opcji -p. Powoduje on, że po wczytaniu każdego wiersza, na zakończenie niejawnie działającej pętli (tej samej co po opcji -n), dodatkowo wypisywana jest wartość zmiennej domyślnej $_. Program w rzeczywistości wygląda zatem tak: while(<STDIN>){ 1; } continue{ print $_; } przy czym zamiast standardowego wejścia, czytanie odbywa się bezpośrednio z pliku (ponieważ podano nazwę pliku jako argument w wierszu poleceń.
    [c] W tym przypadku kod programu jest trywialnie prosty. Oto podano tylko opcję -e, zatem cały skrypt wykona się tylko raz. Skryptem jest podana po opcji -e treść print `cat nazwiska.txt`. Polecenie to działa tak:
  3. Które z poleceń usunie wszystkie pliki z katalogu domowego użytkownika?

    a) perl -e 'system ”rm -rf $HOME”'

    b) find $HOME -print0 | perl -0ne unlink

    c) find $HOME -print -exec 'perl unlink'

    d) perl -ne 'unlink' < find $HOME -print0

    Prawidłowe odpowiedzi to [a] i [b]. Nieprawidłowe są [c] i [d].
    [a] zadziała dlatego, że wykonujemy program z poleceniem system, które uruchamia polecenie systemowe (podobnie trochę jak odwrotne apostrofy). Polecenie to rm -rf $HOME, a działa ono tak, że wykasuje wszystko co znalezione zostanie na ścieżce $HOME i do czego będzie dostęp. Dlatego odpowiedź ta jest prawidłowa.
    [b] to typowy przykład zastosowania opcji -0 w perlu (co było tłumaczone wiele razy - sprawdź zadania i przykłady).
    Pozostałe możliwości są bez sensu i nie zadziałają. W odpowiedzi [c] wykonanie perla przez -exec nie powiedzie się, ponieważ nie zostały perlowi przekazane wszystkie argumenty. Prawidłowo można napisać -exec 'perl unlink' {} \; ale nawet nie sprawdzam czy to zadziała. Nie chcę stracić przypadkiem wszystkich plików :) Odpowiedź [d] jest jeszcze bardziej nieprawidłowa.
  4. Kompilację programu z jednoczesnym trybem raportowania błędów robi się poprzez uruchomienie programu z opcjami:

    a) perl -c program.pl

    b) perl -c --Wall program.pl

    c) perl -ne 'cat program.pl'

    d) perl -Wc program.pl

    Prawidłowa odpowiedź to [d]. Pozostałe są nieprawidłowe.
    [d] jest prawidłowa ponieważ w perlu istnieje opcja -w oraz -W i obie robią praktycznie to samo: włączają ostrzeżenia (-W jest bardziej wrażliwa i raportuje także na temat bibliotek systemowych perla). Opcja -c to opcja, która wymusza kompilację. Samodzielnie umieszczona w wywołaniu takim jak np. perl -c program.pl nie przyda się do wyświetlenia ostrzeżeń (info dotyczyć będzie tylko powodzenia kompilacji). Dlatego typowym zastosowaniem jest użycie kombinacji -wc lub -Wc, i jest to jedyna możliwość pasująca do ostatniej odpowiedzi.
  5. Wartością wyrażenia 1/2+4+4/2/2**2 jest:

    a) 4.500

    b) 0.125

    c) 5.500

    d) 5.000

    Prawidłowa jest oczywiście odpwiedź [d]. Pozostałe nie są prawidłowe.
    Dzieje się tak z powodu kolejności obliczania wyrażeń. Trzeba znać zwykły priorytet operatorów perla, i wtedy nie ma żadnych problemów z przewidzeniem jaki będzie wynik. Oto kolejne etapy obliczania tego wyrażenia, zapisane za pomocą nawiasów dla odtworzenia rzeczywistej kolejności działań: ( (1/2) + 4 )   +   ( (4/2) / (2**2) ) (  0.5  + 4 )   +   (   2   /   4    ) 4.5 + 0.5
  6. Wynikiem operacji 1^(3&!2) jest:

    a) 0

    b) 1

    c) 2

    d) błąd perla

    Prawidłowa odpowiedź do [b]. Pozostałe są błędne.
    Rozwiązanie tego zadania należy zacząć od zrozumienia, co zostało właściwie napisane. Mamy tu na pierwszy rzut oka przypadek operacji bitowych, które mają bardzo podobną kolejność wykonania. Kluczem do znalezienia wyniku jest oczywiście obliczenie tego, co znajduje się w nawiasie. A nawias to inaczej bitowy iloczyn liczby 3 oraz tego, co pozostanie po logicznym zanegowaniu liczby 2. Tak się składa że negacja tej liczby zwraca ZERO ponieważ liczba 2 jest uznawana za wartość typu 'prawda'. Zatem mamy 3 & 0, co oczywiście sprowadza nas do zera, które xorowane z liczbą 1 daje w wyniku od razu 1. Kluczem do sprawy jest zauważenie, że negacja liczby 2 nie następuje w tym wyrażeniu na bitach, lecz ewaluowana jest logicznie. Od tego zależą dalsze losy wyrażenia. Cały dowcip polega na tym, że operator wykrzyknika ! oraz jego słabszy odpowiednik not, to NIE SĄ OPERATORY BITOWE! Tylko logiczne, i działają zgodnie z tym, co powinny robić. Proszę pamiętać... negacja bitowa w perlu to operator ~. :) Dla wyrażenia 1^(3&~2) wychodzi zero od razu, zgodnie z działaniem operatora ~.
  7. Wynikiem działania kodu: $a=0; print +($a++ + $a--) ? $a++ : $a--; jest liczba:

    a) -2

    b) -1

    c) 0

    d) 1

    Prawidłowa odpowiedź: [c], pozostałe są nieprawidłowe.
    W tym przypadku znaczenie ma po prostu kolejność wykonywania operacji. Popatrz na nawias w wyrażeniu warunkowym. Zaczynając od zera, pierwsze wyrażenie z postinkrementacją zwróci 0 i zwiększy wartość zmiennej $a. Drugie wyrażenie zwróci wartość 1 zmiennej $a i następnie zmniejszy jej wartość z powrotem do zera. W wyniku tego, wewnątrz nawiasów obliczona zostanie wartość 1, ze zmienną $a równą na końcu efektywnie 0. Zwrócona zostaje z wyrażenia warunkowego wartość operacji $a++, która dla zmiennej $a równej 0, wynosi 0. Zmienna $a zostaje zwiększona dopiero potem, ale tego już polecenie print nie wypisze.
  8. Które polecenie zadziała prawidłowo:

    a) return $wynik or die „Błąd”;

    b) print $wynik || die „Błąd”;

    c) $wynik = 0 && return $wynik;

    d) $wynik = $wynik or 0;

    Prawidłowa odpowiedź to [b] i tylko ona.
    Wyjaśnienie wszystkich tych przypadków jest proste. Każdy z nich odpowiada jakiemuś zamysłowi programisty, i dość łatwo zrozumieć jaki to był zamysł. Oto w punkcie [a] należało zwrócić wynik przechowywany w zmiennej $wynik lub spowodować zakończenie programu przez die. W punkcie [b] zamiarem programisty było wypisanie wartości zmiennej $wynik lub uśmiercenie programu. W punkcie [c] zamiarem miało być zapewne wyzerowanie zmiennej $wynik i zwrócenie jej przez return (zapewne wewnątrz jakiejś źle napisanej funkcji), a w punkcie [d] ktoś chciał błysnąć elokwencją perlową i ustawiać zmienną $wynik na zero, jeżeli zmienna ta nie była ustawiona (czyli mogła np. wynosić undef lub zero, lub być pustym napisem). Każdy z tych przypadków jest bliski rzeczywistości, ale jak to zwykle bywa, perl wykona to, co napiszemy, a nie to, co chcemy, żeby wykonał. :)
    W punkcie [a] błąd polega na tym, że najpierw wykona się polecenie return, niezależnie od wartości zmiennej $wynik, dlatego nigdy nie dojdzie do wykonania części z die. Po prostu, zanim perl będzie miał okazję tę część wykonać, wykonany zostanie powrót z funkcji, w której taka konstrukcja została umieszczona. Jest tak także na skutek niskiego priorytetu operatora or. W punkcie [b] wszystko odbywa się prawidłowo, z tym, że całość działa trochę na zasadzie efektu ubocznego: jeżeli zmienna $wynik nie będzie miała prawdziwej wartości, wykona się die, i polecenie print napisałoby zwracaną przez die wartość, gdyby... gdyby zdążyło, ale nie zdąży, bo die zakończy program przed zwróceniem wartości. Dlatego ten przykład działa zgodnie z zamierzeniem programisty, ale mimo wszystko jego wykonanie odbywa się "na krawędzi" poprawności. Mimo to, spośród wszystkich odpowiedzi do wyboru, ta jest najbardziej prawidłowa, ponieważ przynajmniej zadziała tak, jak wygląda, że zadziała. Punkt [c] zawiera wyrażenie w postaci 0 and, a takiego wyrażenia perl w ogóle nie oblicza, bo wiadomo z góry, że zero andowane z czymś, to nadal zero. Użycie takiego zapisu na końcu funkcji ma dodatkowe efekty uboczne, ponieważ rzeczywiście może zostać zwrócone 0, ale nie na skutek polecenia return lecz dlatego, że ostatnim obliczonym wynikiem jest przypisanie zera do zmiennej $wynik. Mowa oczywiście o przypadku funkcji w postaci: sub BLA{ $wynik = 0 && return $wynik; } Punkt [d] nie zadziała prawidłowo, ponieważ jest to wyrażenie typu coś OR zero, a to z reguły nigdy nie oblicza tej części z zerem. Dlatego ustawianie wyniku w ten sposób jest bezcelowe. Poza tym należy zastosować operator o wyższym priorytecie - ||, gdyż or wiąże słabiej od operatora przypisania i w efekcie wyrażenie jest wykonywane tak jakby stały nawiasy: ($wynik = $wynik) or 0.
  9. Interpolacja zmiennej $a występuje w przypadku:

    a) $a.”abc123”

    b) ”$abc123”

    c) 'abc$a123'

    d) ”${a}bc123”

    Prawidłowa jest tylko odpowiedź [d]. Pozostałe przypadki nie powodują interpolacji zmiennej $a. W punkcie [a] mamy wartość zmiennej połączoną (skonkatenowaną) z napisem, w którym jednak nie ma żadnej zmiennej, więc napis ten nie interpoluje niczego. Sklejenie odbywa się po uprzedniej zamianie wartości zmiennej $a na tekst, ale nie jest to interpolacja. W punkcie [b] może zajść próba interpolacji, ale nie zmiennej $a, lecz zmiennej $abc, ponieważ perl wyszukuje w napisie zawsze najdłuższy token. Zatem interpolacja zmiennej $a nie zachodzi w tym przypadku, bo w ogóle nie ma w nim zmiennej $a. Punkt [c] jest doskonałym przykładem na złapanie nieuważnych - jest to napis cytowany apostrofem, który wyłącza całkowicie interpolację, i w efekcie treścią tego napisu jest dosłownie to, co zostało napisane, a więc 'abc$a123' (8 znaków). Nie zachodzi tu interpolacja. W przypadku [d] zajdzie prawidłowa interpolacja zmiennej $a, reprezentowanej tu przez klamry wyłuskujące właściwą nazwę zmiennej z napisu. Była o tym mowa na wykładzie, sprawdź także niektóre ćwiczenia. Okazało się że to pytanie stało się pułapką dla wielu. :)
  10. Prawidłowy składniowo jest zapis:

    a) $ab = ”abc${${ab}c}abc”;

    b) @a{@a} = @a;

    c) @a[@a] = [ ] x @a;

    d) $::a = $'a + $main::a;

    Prawidłowe są odpowiedzi [b], [c] i [d]. Odpowiedź [a] jest nieprawidłowa.
    Wszystko dlatego, że pytanie było o poprawność składni, a ta jest jak widać bardzo elastyczna. W punkcie [a] błąd jest w miejscu zapisu $ab = "abc${${ab}c}abc";. Perl nie rozumie wyróżnionego fragmentu. W punkcie [b] do wycinka hasza %a indeksowanego elementami tablicy @a przypisywana jest tablica @a. Dla tablicy @a=(1,2,3) spowoduje to utworzenie hasza %a = (1=>1, 2=>2, 3=>3);. Sposób ten był wielokrotnie demonstrowany na wykładzie i ćwiczeniach, więc tutaj nie powinno być problemów (chociaż były). Punkt [c] to zapis, który nie do końca działa ale jest prawidłowy składniowo, i należy przez to do grupy poprawnych odpowiedzi. To co się wydarzy, jest po prostu zlepkiem różnych akcji, bez żadnej ukrytej magii. Najpierw popatrz na prawą stronę. Masz tam wyrażenie [] x @a. Wartością tego wyrażenia będzie napis tekstowy, podobny do np. "ARRAY(0x51a1e0)ARRAY(0x51a1e0)ARRAY(0x51a1e0)", dlatego, że operator x (powtarzania) działa na tekstach (!). Jego użycie w tym kontekście spowoduje, że anonimowa tablica [] zostanie zamieniona na wartość tekstową pochodzącą od referencji do niej, i ten napis będzie powtórzony trzykrotnie. Po prawej stronie mamy zatem wartość skalarną, która w końcowym etapie będzie zwykłym napisem. OK, teraz spójrz na lewą stronę operatora przypisania. Stoi tam jak wół wyrażenie @a[@a]. Zapewne przypominasz sobie coś bardzo podobnego, to znaczy wycinek tablicy. W przypadku wycinka, notacja jest dokładnie taka sama, czyli @nazwa[...]. Pozostaje tylko zastanowić się, co stanie się z tablicą umieszczoną w nawiasach kwadratowych. Czy jest tam kontekst skalarny, czy listowy? Od tego zależy wartość, jaka zostanie tam wpisana. Okazuje się, że dla wycinka tablicy w nawiasach kwadratowych istnieje kontekst listowy (ponieważ perl spodziewa się tam otrzymać listę indeksów), zatem tablica @a rozwinie się do listy wartości (1,2,3), które zostaną użyte jako indeksy. Indeksować one będą drugi, trzeci i (nie istniejący) czwarty element tablicy @a, czyli tej samej, z której pochodzą. Do pozycji wskazywanych przez te indeksy będzie przypisana wartość znajdująca się po prawej stronie operatora przypisania (która jest prostą wartością skalarną - i do tego tekstem). W przypadku przypisania skalara do tablicy (w tym właśnie przypadku to następuje), zostaje on umieszczony w pierwszym możliwym do ustawienia elemencie, natomiast pozostałe elementy, jeżeli istnieją otrzymują wartość undef. Dlatego całe to wyrażenie, dla przykładowej tablicy @a = (1,2,3); ma swoją bardziej rozwiniętą postać: ($a[1],$a[2],$a[3]) = 'ARRAY(0x51a1e0)ARRAY(0x51a1e0)ARRAY(0x51a1e0)'; Takie wyrażenie powoduje, że w tablicy @a na pozycji drugiej (indeks 1) zapisuje się pokazany napis, natomiast pozycja trzecia i czwarta uzyskują w momencie tego przypisania wartości undef. Końcowa postać tablicy @a jest zależna jednak od tego, co w niej znajdowało się początkowo, to znaczy jakie tam są wartości. W pewnych przypadkach taki kod może nie zadziałać, ale przy założeniu, że tablica zawiera liczby - przykład będzie prawidłowy.
    W punkcie [d] ma miejsce zwykłe sumowanie trzech zmiennych, z których każda może być tą samą zmienną, tylko różnie zapisaną. Forma $::a to skrócona postać zapisu $main::a, również użytego w tym wyrażeniu. Notacja $'a to stara forma tego samego zapisu, co $main::a. Zatem logicznie, zapis ten jest równoważny napisaniu np. $a *= 2.
  11. Obiekt [1,2,3] to:

    a) zwykła tablica

    b) tablica anonimowa

    c) zwykła lista

    d) lista anonimowa

    Tutaj nie ma żadnych niespodzianek, ani wątpliwości. Prawidłowa odpowiedź to [b] - tablica anonimowa. Poznajemy to po nawiasach kwadratowych i liście wewnątrz nich. I tyle.
  12. Jaka jest wartość listy w kontekście skalarnym?

    a) jest to liczba elementów listy

    b) jest to ostatni element listy

    c) lista nie ma kontekstu skalarnego

    d) standard perla 5.8+ tego nie precyzuje

    Prawidłowe odpowiedzi, to [b] i [c]. Lista jako lista, nie ma w perlu kontekstu skalarnego. Tłumaczone to było na dwóch wykładach i znalazło się w zadaniach ćwiczeniowych. Dodatkowo, gdy listę umieścić w kontekście skalarnym, na skutek działania operatora przecinka zwracana jest wartość umieszczona w liście najbardziej z prawej strony (ostatnia).
  13. Wskaż operacje nie zmieniające tablicy:

    a) push @T, pop @T;

    b) shift @T, push @T;

    c) pop @T, unshift @T;

    d) unshift @T, shift @T;

    Prawidłowe są odpowiedzi [a] i [d]. Pozostałe są nieprawidłowe. Zachowawczość tablicy wynika z charakteru operacji, które we wszystkich tych przykładach są na niej wykonywane. W punkcie [a] do tablicy na koniec jest wkładany (push) element zdjęty z jej końca (pop). Nie zmieni to tablicy. W punkcie [b] z tablicy jest usuwany pierwszy element a na ostatnią pozycję jest wstawiana wartość undef. W punkcie [c] obie instrukcje są podobnie bez sensu połączone: z tablicy jest zdejmowany ostatni element, ale na początek jest wstawiana wartość undef. W ostatnim punkcie, [d] jest wszystko prawidłowo, a tablica nie zmienia się: do tablicy na początek wstawiany jest (unshift) element, który zdjęto (shift) z początku tablicy.
  14. Dereferencja to:

    a) zamiana tablicy na skalar-referencję

    b) zamiana referencji na obiekt wskazywany

    c) zamiana referencji na dereferencję

    d) zamiana skalara na referencję do niego samego

    Prawidłowa odpowiedź to [b]. Pozostałe są bez sensu.
  15. Wynikiem działania kodu: @a=1..2; @a=@a x 2; print ”@a”; jest napis:

    a) 1 2 1 2

    b) 1 2

    c) 22

    d) 11

    Prawidłowa odpowiedź to [c]. Pozostałe to podpucha :) Znowu sekret kryje się w zrozumieniu jak działają operatory w wyrażeniach perla. Mamy w tym kodzie tablicę @a, która ma elementy (1,2). Do tej tablicy zostaje następnie przypisany wynik wyrażenia (a więc poprzednia zawartość zniknie!) @a x 2. Ponieważ operator x działa wyłącznie na napisach, tablica zostaje zamieniona na napis w kontekście skalarnym (ponieważ operator ten wymaga skalara do pracy). Wartością tablicy w kontekście skalarnym będzie liczba jej elementów, czyli wartość 2. Wykonywane jest zatem wyrażenie w postaci "2" x 2, którego wartością jest 22 i to jest poprawna odpowiedź. Odpowiedź [a] nie jest poprawna, ale BYŁABY, gdyby dodać nawiasy w wyrażeniu powtarzanym (zmiana kontekstu - tablica stałaby się listą elementów, i operator podwoiłby listy): @a = (@a) x 2;. Od malutkich nawiasów może czasem zależeć tak wiele! :)
  16. Wynikiem działania kodu: $a = sub{ return [ ]; }; print $a; jest napis, zawierający słowo:

    a) REF

    b) CODE

    c) ARRAY

    d) ARRAYREF

    Prawidłowa odpowiedź to oczywiście [b]. To zadanie jest trywialne. Chodzi o wypisanie prostej refencji, która zamieniona na tekst ma zawsze postać zawierającą wyraz oraz adres w pamięci. Dlatego w pytaniu jest napisane "napis, zawierający słowo". Do zmiennej $a przypisywana jest wartość skalarna, będą referencją do kodu, ponieważ po prawej stronie zdefiniowano anonimową funkcję (sub{ ... }). To co znajduje się w funkcji, to już sprawa wtórna. Gdyby jednak za pomocą zmiennej $a wywołać funkcję, wynikiem była referencja do anonimowej tablicy: $a->();, lub &$a - zwróciłyby napis ze słowem ARRAY.
  17. Podczas używania pragmy kompilatora use strict, dozwolone (czyli nie generujące żadnego ostrzeżenia) są następujące przypisania:

    a) $main::abc = 1;

    b) $abc = 1;

    c) my $abc = 1;

    d) our $abc = 1;

    Prawidłowe odpowiedzi to [a], [c] i [d]. Odpowiedź [b] jest nieprawidłowa, ponieważ przy aktywnej pragmie use strict; nie jest dozwolone używanie niekwalifikowanych zmiennych, to znaczy takich, które nie mają dodanej nazwy pakietu. Dlatego też zadziała bez ostrzeżeń i całkiem prawidłowo przypisanie do zmiennej globalnej w punkcie [a], do zmiennej leksykalnej w punkcie [c] oraz do zmiennej pakietowej w punkcie [d].
  18. Zaznacz, która funkcja napisana jest prawidłowo, to znaczy – zadziała zgodnie z nazwą:

    a) sub silnia ( n ) { if ( n < 1 ) return 1; else return n * silnia(n-1); }

    b) sub silnia { return $_[0]>1 ? $_[0]*silnia($_[0]-1):1; }

    c) sub twice { 2*$_[0] }

    d) sub suma { my $suma = 0; map{ $suma+=$_ } @_; }

    Prawidłowe odpowiedzi to tylko [b] i [c]. Odpowiedź [a] to w ogóle nie jest kod perla (!) i dało się na to nabrać sporo osób... które pewnie niewiele kodów perla w ogóle widziało... ekhm. Odpowiedź [b] to prawidłowo zbudowana funkcja, która w dodatku dobrze oblicza silnię dla niewielkich liczb naturalnych. W przypadku podania wartości ujemnych zwracana jest po cichu liczba 1. Punkt [c] to malutka funkcja, której wynikiem jest podwojony arytmetycznie argument. Dla wartości tekstowych funkcja ta wywoła błąd, ale tutaj uznać można ją za prawidłową (silnia także przecież nie zadziała dla wartości tekstowych, ale się tym nie przejmujemy). W punkcie [d] jest najciekawsza funkcja, ponieważ mimo pozornie prawidłowej składni, nie będzie ona zwracać prawidłowego wyniku sumy. Powodem jest brak instrukcji return, które jasno określiłoby, co ma zostać zwrócone. W efekcie funkcja zwraca wartość pochodzącą z działania operatora map, a jest to wartość nie mająca nic wspólnego z sumą elementów.
  19. Wykonano kod $a=1;$b=2;$a+=$b-=$a+=$b; Jakie będą nowe wartości zmiennych $a i $b?

    a) 1 i 2

    b) 2 i 3

    c) 2 i -1

    d) -1 i 0

    Prawidłowa odpowiedź to [c]. Zadanie to rozwiązała nawet moja żona, która (założę się) nie zna perla bardziej niż typowy student przedmiotu monograficznego Perl-Programowanie :) W każdym razie, kolejność działań w tym wyrażeniu jest taka: $a=1;$b=2;$a+=($b-=($a+=$b)); a to prowadzi nas do rozwiniętego zapisu: $a=1;$b=2;$a=$a+($b=$b-($a=$a+$b)); co jest już łatwo obliczyć: $a = $a + ($b=$b-($a=1+2)); # $a jest równe 3 $a = $a + ($b=2 - 3); # $b staje się równe -1 $a = 3 + (-1); # no i $a staje się równe 2 Prawda, że proste zadanie?
  20. Wskaż, który moduł (zapisany w pliku Bla.pm) jest poprawnie napisany:

    a) package Bla; sub Bla{ 1 } 1;

    b) package Bla; no strict; sub Bla{ 1 } 1;

    c) package Bla; use base 'Exporter'; sub Bla{ 1 } 1;

    d) package Bla; our @ISA='Exporter'; sub Bla{ 1 } 2;

    W tym zadaniu prawidłowe składniowo są wszystkie przykłady. Oczywiście gdyby zastanawiać się nad poprawnością kodu od strony logicznej, to z tą poprawnością już wcale nie jest tak optymistycznie. Poniżej krótka dyskusja przykładów:
    Punkt [a] to typowa postać pakietu mogącego być klasą. Jest taka niepisana zasada, że klasy obiektowe nie powinny niczego eksportować do przestrzeni symboli innych pakietów. Punkt [b] także przedstawia taką klasę, w której dodatkowo wyłączono jawnie pragmę strict. Jest to przeciwieństwo użycia use strict; i działa w obszarze aktualnego bloku lub poza blokiem - od miejsca deklaracji do końca pliku. Punkt [c] to początek pakietu lub klasy, która dziedziczy po pakiecie Exporter. Niewiele z tego dziedziczenia wynika, ponieważ w treści nie ma żadnych następnych wzmianek na temat eksportowania zmiennych. Niemniej, odziedziczona metoda import, pochodząca z pakietu Exporter będzie działać, chociaż nie będzie miała co robić. Do jej działania wymagane są bowiem tablice @EXPORT lub @EXPORT_OK lub hasz %EXPORT_TAGS. Kod pokazany w punkcie [d] to właściwie to samo, co kod z punktu [c], z tym, że zamiast use base użyto tam deklaracji zmiennej pakietowej @ISA do której wpisano moduł, po którym pakiet będzie dziedziczył. Jest to starsza metoda określania dziedziczenia, należy obecnie używać konstrukcji typu use base, gdyż wykonuje się ona w momencie kompilacji kodu, a nie w momencie pracy programu. W punkcje [c] i [d] brakuje deklaracji użycia modułu Exportera, use Exporter;, ale mimo to składnia modułów jest prawidłowa.
  21. Zmienne leksykalne deklaruje się przez 'my' a zmienne o zasięgu dynamicznym przez 'local'.

    a) prawda w całości!

    b) nieprawda w całości (ściema!)

    c) prawda tylko dla zmiennych leksykalnych

    d) prawda co do zmiennych dynamicznych

    Prawidłowa odpowiedź to [a], pozostałe są nieprawidłowe w części lub w całości. Jest to sprawa oczywista, że zmienne leksykalne deklaruje się przez my, natomiast dynamiczne przesłanianie zmiennych robi się za pomocą local. Warto wspomnieć o ważnej różnicy - local można stosować nawet dla elementów składowych, nie tylko dla całych zmiennych, np. można chwilowo przesłonić wartość jakiegoś elementu tablicy: local $T[1] = undef; Nie trzeba dodawać, że tablica @T powinna istnieć (w przeciwnym wypadku zostanie utworzona), ale ten sposób NIE zadziała z oczywistych względów po zastosowaniu my lub our. Dyrektywa local to po prostu coś, co powinno nazywać się temporary_global :)

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 Matematyki - Zakład Informatyki - Strona domowa - Perl - Kolokwia
[c] Piotr Arłukowicz, materiały z tej strony udostępnione są na licencji GNU.