Zadania do wykładu o operatorach perla + dodatkowe

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

Perl: programowanie - Zadania do wykładu o operatorach perla + dodatkowe

W tym zestawie zadań znajdziesz także zadania dotyczące priorytetów operatorów. W razie potrzeby cała dokumentacja zawarta jest w perldoc perlop i proponowanych podręcznikach książkowych. Zadania niniejsze należy wykonać a wyniki opisać w formie raportu, który powinien być przesłany do prowadzącego zajęcia najpóźniej do dnia 20061119 włącznie. Raporty nadesłane po tym terminie uznane będą za nieważne.

  1. Napisz program, który za pomocą operacji dodawania, odejmowania, mnożenia lub dzielenia zrobi z cyfr 1..9 liczbę 100. Odpowiednie operatory należy wstawić pomiędzy kolejne cyfry, tak jak na załączonym obrazku:
    perl -le 'print (1+2+3+4+5+6+7+8*9)'
    Oczywiście, należy znaleźć przynajmniej jeden inny sposób.
    * (hardcore mode) Napisz program, który znajduje wszystkie możliwe układy operatorów przy których zachowana jest suma 100. Pozwól na stosowanie nawiasów. Możesz wykorzystać funkcję eval oraz dowolny deterministyczny algorytm poszukiwania rozwiązania.
    Zadanie to okazało się na tyle ciekawe, że aż kilka osób podjęło się jego realizacji. Program w trybie "hardcore" zrobiły jednak tylko dwie osoby. Rewelacja. Takie zadania będzie trzeba dać na egzaminie (piszę to ze świadomością, że tego nie zrobię, ale działania prewencyjne, takie jak to, mogą sugerować pewne możliwości). Oto jeden z programów podesłany przez studenta, który rozwiązuje zadanie w elegancko zagłębionych pętlach, czysto iteracyjnie, brutalną siłą (program jest ciut poprawiony przeze mnie i z zachowaniem kolorowania składni perla, wynik działania po prawej stronie na obrazku):
    @op = ('+', '-', '*', '/');
    for $a(@op){
      for $b(@op){
        for $c(@op){
          for $d(@op){
            for $e(@op){
              for $f(@op){
                for $g(@op){
                  for $h(@op){
                    $_ = "1${a}2${b}3${c}4${d}5${e}6${f}7${g}8${h}9";
                    if(100 == eval $_){
                      print "$_\n";
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    Brute force
    Osoby pragnące więcej wrażeń mogą przeanalizować program, który generuje rozwiązania metodą rekurencyjną, a dodatkowo sprawdza wszystkie kombinacje jednej pary nawiasów wstawiane w napis już po jego wygenerowaniu. Algorym to prosta jak drut rekurencja z nawrotami (backtrack), a dla każdego znalezionego napisu ze wszystkimi cyframi testowane są wszystkie kombinacje nawiasów. Wynikowy plik z rozwiązaniami zawiera nie tylko dobre rozwiązania, ale w ogóle wszystkie. Format pliku jest taki, że w jednym wierszu jest jedno rozwiązanie, następnie wynik obliczony z działań, a potem informacja czy jest to akceptowane rozwiązanie, czy nie. W przypadku rozwiązań akceptowanych, na końcu wiersza stoi napis "OK". Plik zajmuje 80MB dlatego zamieszczam tutaj jego spakowaną gzipem wersję. Aby zobaczyć wszystkie prawidłowe rozwiązania bez pobierania pliku, wystarczy uruchomić program i przepuścić wyniki generowanego przez niego przez polecenie grep, pisząc choćby 123456789.pl | grep "OK". I gotowe. Oto program (zmienne nazwałem tak, aby wiadomo było co w nich się znajduje):
    #!/usr/bin/perl -w
    
    use strict;
    
    $|=1;  # wylaczenie buforowania terminala
    
    # szukanie rozwiazan z nawiasami od napisu zawierajacego po prostu 1
    szukaj('1');
    
    # budowanie napisu w postaci x+y+z+... itp. ze znakami +-*/
    sub szukaj {
      my $napis = shift;
      my $cyfra = shift || 1;
      my $stary = $napis;
      foreach my $operator ('+','-','*','/'){
        $napis = $stary . $operator . ($cyfra+1);
        if($cyfra+1 < 9){ szukaj($napis,$cyfra+1); }
        else{
          foreach my $przesuniecie (0,2,4,6,8,10,12,14){
            my $gotowy_napis = $napis;
            substr($gotowy_napis,$przesuniecie,0) = '(';
            for(my $i=$przesuniecie+4;$i<19;$i+=2){
              my $z_nawiasem = $gotowy_napis;
              substr($z_nawiasem,$i,0) = ')';
              my $rezultat = eval $z_nawiasem;
              if(!defined $rezultat){ print "$z_nawiasem UNDEF -\n"; }
              elsif($rezultat eq '100'){ print "$z_nawiasem $rezultat OK\n"; }
              else{ print "$z_nawiasem $rezultat -\n"; }
            }
          }
        }
      }
    }
    Brutalna metoda z nawiasami
    Znaczenie poleceń jest takie:
    1. wygenerowanie spakowanego pliku z wynikami z pomiarem czasu
    2. wyświetlenie wielkości w MB spakowanego pliku
    3. policzenie liczby wierszy w pliku wyników
    4. policzenie dobrych rozwiązań w pliku z wynikami
  2. Określ, ile potrzeba operacji dodawania i odejmowania, aby z liczby 997 zrobić liczbę 1000. Można wyłącznie dodać 5, a odejmować wyłącznie 3.
    Zadanie jest trywialne, zwłaszcza po poprzednim. Należy wykonać choćby operację (997+5+5+5-3-3-3-3), która składa się na trzykrotne dodanie 5 i czterokrotne odjęcie 3. Nic ponad to. Tym razem bez filozofii. Warto jednakże zauważyć, że dodawanie i odejmowanie to operatory dwuargumentowe, które obliczane są od lewej do prawej (gdyż w przeciwnym kierunku mielibyśmy jaki wynik?)
  3. Korzystając z wiedzy zawartej w perlop, wyjaśnij, dlaczego polecenie: perl -le 'print (1+1) * 2;' NIE działa zgodnie z przewidywaniami. Oczywiście, sprawdź jak działa, a potem szukaj zrozumienia tego przykładu. Wskazówka: szukaj informacji o operatorach listowych, zachłannie pobierających argumenty.
    Jest to dokładnie wyjaśnione w odpowiedziach do pytań na kolokwium. Perl traktuje w tym przypadku polecenie print jak funkcję, która pobiera swoje argumenty w nawiasie, i tyle. Reszta jest obliczana w kontenście pustym, a na ekranie pojawia się tylko wynik działania print.
  4. Sprawdź, co jest wynikiem cytowanych poniżej programów: a) perl -le '$a=0; print ++$a + $a++'; b) perl -le '$a=0; print $a++ + ++$a'; c) perl -le '$a=0; print $a++ + $a++'; d) perl -le '$a=0; print ++$a + ++$a'; Zastanów się. Możesz być zaskoczony wynikami. Co można powiedzieć o działaniu operatorów pre- i postinkrementacji? Czy perl gwarantuje przewidywalne zachowanie w przypadkach powyższych programów?
    Wyjaśnienie działania tych operatorów jest zawarte w odpowiedziach na zadania z kolokwium nr. 2. Zapraszam tutaj.
  5. Jaki będzie efekt działania operacji dodawania inkrementowanej i dekrementowanej wartości? Na przykład $a++ + $a-- w różnych kombinacjach?
    Odpowiedź na to pytanie także jest zawarta w odpowiedziach do zadań kolokwialnych.
  6. Jaka będzie wartość wyrażeń: $i = $i++; $i += $i--; $i += ++$i; $i -= ++$i - $i--; Czy potrafisz przewidzieć wynik przed jego sprawdzeniem?
    Wyrażenia obliczą się zgodnie z regułami dotyczącymi operatorów perla. Wyniki będą następujące: w kolejności: $i wyniesie 0, -1, 2, 1. Szczegóły pomagające zrozumieć, jak działają takie wyrażenia są tutaj.
  7. Sprawdź, jaki jest wynik operacji potęgowania dla liczby -2 przy podnoszeniu jej do potęgi 2. Jaki powinien być wynik?
    Matematycznie, wynik powinien wynosić 4, ponieważ liczby ujemne przy ponoszeniu do kwadratu stają się dodatnie. Tutaj jednak działają inne priorytety operatorów, niż w matematyce - po prostu potęgowanie ma wyższy priorytet niż operator jednoargumentowego minusa. Stąd, wykonuje się ono jako pierwsze więc w efekcie dostajemy wyrażenie, którego wartością końcową jest -4.
  8. Zaobserwuj, jak zmienia się wartość zmiennej $x, do której wpisano napis "abc" a następnie zinkrementowano dwukrotnie. Co zawiera zmienna $x? Do czego może się przydać taka właściwość perla?
    Działa tutaj "magia" zaszyta w operator inkrementacji (operatory dekrementacji takiej magii nie posiadają). Napis zostanie zinkrementowany w ten sposób, że litery zmienią się na kolejne im odpowiadające znaki z alfabetu. Z cyframi podobnie. W przypadku inkrementacji liter nie ma wartości zerowej. Wynikiem operacji przedstawionej w zadaniu będzie uzyskanie wartości zmiennej $x równej "abe". Tego typu zachowanie perla przydaje się do iterowania po krótkich kombinatorycznych listach znakowych oraz wprost do generowania haseł i numerów porządkowych.
  9. Co będzie wynikiem działania polecenia: perl -le 'print sort(2,1,sort 4,3)'? Mimo, że wynik działania daje się łatwo przewidzieć, wyjaśnij, w jaki sposób odbywa się przekazywanie argumentów do operatora sort.
    Polecenie sort działa tutaj bez nawiasów, zatem traktowane jest przez perla jak operator listowy. Operator taki ma taką własność, że pobiera maksymalnie dużo argumentów (tyle ile może). Tego efektu tutaj nie widać, ale najpierw obliczany jest sort wewnątrz nawiasu, i bierze on dwa argumenty - liczby 4 i 3. Gdyby było ich więcej, wziąłby wszystkie. Wynikiem całego wyrażenia jest oczywiście posortowana lista liczb.
  10. Zapoznaj się z konstrukcjami typu (....lista....)[indeks]. Wykorzystaj tego typu konstrukcję do wypisania nazwy dnia tygodnia w zależności od podanego numeru (indeks może być liczbą z zakresu 0-6, natomiast lista powinna zawierać odpowiednie nazwy dni tygodnia). Oczywiście należy w tym celu napisać prosty programik perlowy, może być jednowierszowy.
    Korzystamy tutaj z własności list, które można tworzyć "w miejscu" i od razu odwoływać się do nich przez bezpośredni iterator o swobodnym dostępie (co sprowadza się do podania numeru pozycji z listy, którą chcemy pobrać). Oto przykłady: perl -le 'print qw(Pn Wt Sr Cz Pt So Nd)[rand(7)]' perl -le 'print +(0..9,"a".."f")[ rand(16) & 15]' perl -lane 'print +(split)[-1]' plik.txt Pierwszy przykład drukuje losowy skrót dnia, wybierany z listy na podstawie numeru porządkowego generowanego przez rand(7) (który zwraca numer od 0 do 6). Drugi przykład losuje jednocyfrową liczbę w systemie szesnastkowym. Trzeci przykład wypisuje ostatnie słowa z każdego wiersza.
  11. Zbadaj i zrozum działanie operatora zakresu. Wyjaśnij jego działanie w dwóch przypadkach: perl -ne 'print if 10 .. 20' plik.txt perl -le 'print for(1 .. 10)'Odpowiednie szczegóły oraz przykłady zastosowania znajdziesz w podręczniku perlop.
    Działanie operatora zakresu jest dwojakie. W pierwszym przypadku w rzeczywistości następuje automagiczne porównanie liczb zakresu z bieżącym numerem wiersza z przetwarzanego pliku (plików). Daje to taki efekt, jakbyśmy napisali: perl -ne 'print if $.==10 .. $.==20' plik.txt W kontekście skalarnym, ten operator zwraca wartość logiczną. Jest on bistabilny, jak przełącznik, i w takim przypadku, jak pokazano, stara się emulować zachowanie programów typu sed, awk i paru innych edytorów zorientowanych wierszowo. Każdy operator .. utrzymuje swój własny stan logiczny. Jest on równy fałsz tak długo, jak lewy operand jest nieprawdziwy. Gdy lewy operand jest prawdziwy, wartością całego operatora jest prawda, tak długo, jak prawdziwy jest też prawy operand tego operatora. Gdy prawy operand przestaje być prawdziwy, cały operator zwraca wartość fałsz. Operator ten przynajmniej raz zwraca wartość prawda, nawet jeżeli był prawdziwy w momencie obliczania lewego argumentu, a przestał być prawdziwy w momencie obliczenia prawego argumentu. Aby nie testować prawego argumentu gdy operator zwróci prawdę, należy zastosować trzy kropki, zamiast dwóch: ... i wtedy prawy operand będzie obliczany dopiero w kolejnym kroku. We wszystkich innych przypadkach operator ... zachowuje się dokładnie tak samo jak .. i w pewnym stopniu mogą one być stosowane zamiennie. W przypadku programu przedstawionego powyżej, operator zwraca fałsz, zanim osiągnięty zostanie odpowiedni zakres wierszy, następnie gdy program jest w obrębie tego zakresu, zwracana jest wartość true, a potem po wyjściu z niego z powrotem wartość false. Dzięki temu można wykonać pewne operacje tylko dla określonego zakresu wierszy w pliku. Drugi przykład to inne zastosowanie operatora zakresu. W kontekście listy operator ten zwraca listę wartości określonych w wywołaniu. Wynikiem działania w powyższym przykładzie będzie wypisanie listy 10 liczb, od 1 do 10 w kolejnych wierszach.
  12. Wyjaśnij działanie operacji: (($a += 2) *= 3) /= 9;Jeżeli wartość zmiennej $a na początku wynosiła 10, to ile będzie równa po zakończeniu cytowanej operacji?
    Wyrażenia w których wykorzystany jest operator przypisania, mają same z siebie wartość przypisania. Zatem zgodnie z priorytetami operatorów, wykonywać się tutaj będą najpierw nawiasy, a wynik z nich pochodzący zostanie użyty do obliczenia następnych/pozostałych wyrażeń. Wyniki będą miały kolejno postać następujących wyrażeń zredukowanych: (($a += 2) *= 3) /= 9; (($a = $a+2) *= 3) /= 9; (($a = 12) *= 3) /= 9; ($a = $a * 3) /= 9; ($a = 36) /= 9; $a = $a / 9; $a = 36 / 9; $a = 4;
  13. Sprawdź, w jaki sposób należy zapisać test na bitach, np. sprawdzenie czy jest ustawiony drugi bit w zmiennej. Typowy zapis zapożyczony z języka C wygląda tak: if($x & 2 == 0){...}else{...}. Co można powiedzieć o priorytecie operatora ==?
    W przypadku perla priorytet operatora == jest większy niż operatora koniunkcji logicznej &, dlatego wyrażenie wykona się tak, jakby było zapisane z nawiasem: $x & (2==0), co sprowadza się do postaci $x & 0, co z kolei jest zawsze 0. W tym przypadku niezbędne są nawiasy, postawione tak, aby działanie zostało wykonane prawidłowo: if( ($x & 2) == 0){ ... } else{ ... }
  14. Przeanalizuj działanie operatora trójargumentowego. Wyjaśnij faktyczne działanie w przypadkach: $a = $test ? $b : $c; ($a_or_b ? $a : $b) = $c; $a % 2 ? $a += 10 : $a += 2
    Operator ten można wykorzystać do zwykłych operacji arytmetycznych, gdzie potrzebne jest zwrócenie konkretnego wyniku, ale także i w przypadkach, gdy konkretny wynik jest potrzebny jako wartość, ale wykonuje się raczej działania dla ich efektów ubocznych. Przykładem prostego zastosowania jest pierwszy wiersz z zadania, gdzie na podstawie wartości zmiennej $test jest ustalana wartość zmiennej $a. Drugi przykład to wykorzystanie operatora trójargumentowego jako operatora lewostronnego wykorzystanego w przypisaniu. Efekt działania takiego kodu to przypisanie wartości zmiennej $c do zmiennej $a lub do zmiennej $b, zależnie od wartości zmiennej $a_or_b. To ma szansę działać ze względu na zastosowane nawiasy i może być podstawą paru ciekawych, hakerskich skryptów, których nikt nie rozumie poza autorem (a i autor rozumie kod tylko przez pierwszych 5 minut po jego napisaniu). Trzeci przypadek jest użyty w podobnym kontekście, zamiast regularnej instrukcji warunkowej. Zapis jest błędny, to znaczy, perl zrobi coś innego, niż można się spodziewać bez analizy priorytetów operatorów. Niespodziewanie, wyrażenie to przetworzone zostanie jako ($a % 2 ? $a+=10 : $a) += 2. Aby mieć pewność w takich przypadkach, co zostanie wykonane, warto zastosować nawiasy: ($a % 2) ? ($a += 10) : ($a += 2) Taki przypadek, w którym modyfikowana jest tylko jedna zmienna można uprościć do wyrażenia pierwszego typu: $a += ($a % 2) ? 10 : 2; Jest to prostsze i czytelniejsze.
  15. Jaka jest różnica pomiędzy operatorem or a ||? Wyjaśnij na przykładzie, kiedy wykorzystanie or może dać gorsze efekty niż w przypadku ||.
    Chodzi tutaj o priorytety operatorów. Operator or (podobnie jak not, xor oraz and mają super niski priorytet. To oznacza, że należy rozsądnie gospodarować operatorami o wyższym priorytecie, takimi jak && lub ||. Widać to wyraźnie choćby w przypisaniu z wartością domyślną, gdzie należy wykorzystać operator || zamiast or: $a = $b || 'default'; $a = $b or 'default' W pierwszym przypadku, ze względu na priorytety operatorów, operacja zostanie wykonana prawidłowo - zmiennej $a zostanie nadana wartość zmiennej $b tylko wtedy, jeżeli ta zmienna istniała i miała wartość różną od napisu pustego lub od zera, w przeciwnym wypadku zmienna $a otrzymywała wartość domyślną "default". Drugi przypadek nie działa tak, priorytet operatora or jest zbyt niski, i w efekcie prowadzi to do wyrażenia: ($a = $b) or "default" co jest zupełnie innym kodem, wykazującym najprawdopodobniej nie to działanie, o które chodziło.
  16. Jeżeli jeszcze nie przyszło Ci to do głowy, to spróbuj wykonać operacje logiczne na napisach! Przykładowo, sprawdź, co będzie wynikiem operacji XOR, OR oraz AND na tekstach "ab  " oraz "  cd".
    Operatory bitowe stosowane na napisach wykonują swoje operacje tak, jakby pracowały na ciągach bitów. Napis jest traktowany jak wektor bitowy. Umożliwia to łatwe wykonywanie operacji bitowych na ciągach bitowych dowolnej długości. W przypadku zadania, wynikiem będą napisy: "ab  " ^ "  cd" == "ABCD" "ab  " & "  cd" == "    " # cztery spacje "ab  " | "  cd" == "abcd" Zwróć uwagę na to, że w napisach są DWIE spacje, nie jedna (przy kopiowaniu tekstu z przeglądarki, może zrobić się z nich nagle jedna spacja, wtedy przykład nie będzie dawał takich samych napisów wynikowych).

Zadania dodatkowe

  1. Sprawdź priorytety operatorów i upewnij się, że rozumiesz działanie perla w poniższych przykładach i jesteś w stanie wskazać, które wyrażenia działają najprawdopodobniej błędnie. Są to dość nietypowe przypadki, i w każdym kryje się jakiś "kruczek": print rand 10 + 30; print rand(10) + 30; chdir 'bla' || die "Error: $!\n"; chdir('bla') || die "Error: $!\n"; print "2+3",exit; print (2+3),exit; print +(2+3),exit;
    Wyjaśnienie dla tych przypadków leży znowu w priorytetach operatorów. Pierwszy przykład, rand 10 + 30, to tak naprawdę wyrażenie to najpierw wykonuje dodawanie liczb, a następnie losowanie (rand 40). Wynikiem jest liczba pseudolosowa z przedziału lewostronnie domkniętego [0,40).
  2. Wypróbuj operatory testowania plików. Wykorzystaj je na przykład do tego, aby testować wybrany plik, w taki sposób: perl -le 'print "to jest plik binarny" if -B "/tmp"' Celowo testujemy katalog "/tmp", aby zauważyć, że w rzeczywistości jest to plik. Niemniej, sprawdź działanie testu -f oraz -d, aby mieć pewność, co do prawdziwej interpretacji tego, czym jest /tmp.
    Wyniki testow /tmp Wyniki działań przeprowadzonych na katalogu. Prawidłowo zwracana jest informacja perla na temat testu z operatorem -d.
  3. Wykorzystując poznane wcześniej polecenie find, które, po zastosowaniu odpowiednich opcji, wypisuje nazwy plików oddzielone znakiem o kodzie zero, zastosuj perla oraz operatory testowania plików, aby wyświetlić wszystkie pliki ze swojego katalogu domowego i wypisać czas ostatniego do nich dostępu w dniach. Jeżeli wypisywane wyniki dodatkowo posortujesz za pomocą polecenia sort, możesz dowiedzieć się, jakie są twoje najstarsze pliki. W ten sposób prowadzący znalazł w swoim katalogu domowym pliki z 2001 roku!
    Wykorzystamy znaną już z wcześniejszych przykładów możliwość polecenia find polegającą na wypisaniu wszystkich nazw plików oddzielonych znakiem o kodzie 0. Strumień danych z polecenia find podzielimy na poszczególne wiersze i każdy z wierszy przekażemy do operatora -M, sprawdzającego czas ostatniej modyfikacji pliku. Wynikiem jest liczba rzeczywista oznaczająca jak dawno temu, licząc w dniach, zmienił się plik. Jest to bardzo przydatne w niektórych zastosowaniach webowych. find . -print0 | perl -ln0e 'print -M $_," $_"' Liczba wypisana przed nazwą pliku to właśnie czas upływający od jego ostatniej modyfikacji. Ogólna liczba wypisanych linii to oczywiście liczba wszystkich plików znalezionych przez polecenie find. W swoim katalogu domowym znalazłem ponad 76 tysięcy plików :) Aby posortować tak wypisywane wyniki, można wykorzystać zewnętrzny program sortujący (polecenie sort), na przykład przekierowując do niego to, co wypisuje perl: find . -print0 | perl -ln0e 'print -M $_," $_"' | sort -n Opcja -n dodana została do polecenia sort w tym celu, aby wymusić sortowanie numeryczne danych. Domyślnie sort sortuje wiersze zakładając, że mają wartość tekstową. Opcja -n zmienia to zachowanie, a sort traktuje pierwszą kolumnę z każdego wiersza jak liczbę, i sortuje według wartości liczbowej, a nie tekstowej.
  4. Operatory testowania plików niekiedy można przyspieszyć, jeżeli wykonuje się kilka testów na jednym i tym samym pliku. Aby to zaobserwować, przeanalizuj wykonanie programów: time perl -e '$a = "/tmp"; stat $a; for(1..100000){ -f $a and -r $a and -w $a and -x $a }' time perl -e '$a = "/tmp"; stat $a; for(1..100000){ -f _ and -r _ and -w _ and -x _ }' Zależnie od obciążenia systemu, można zaobserwować różnicę rzędu wielkości, w przyspieszeniu skryptu. Zaawansowani programiści perla mogą wykorzystać moduł Benchmark do dokładnego pomiaru czasu. Tajemniczy element "_", jest właśnie tym, co odpowiada za przyspieszenie programu.
    Różnica może być całkiem spora, na przykład dla 1e7 powtórzeń otrzymałem takie wyniki (katalog /tmp był już wcześniej kilka razy "odwiedzany"):
    Czasy dostępu do /tmp Czasy dostępu do /tmp bez cache wewnętrznego funkcji stat oraz razem z takim cache. Różnicę w czasie wykonania programu pokazuje polecenie time.
    Dokładne pomiary wykazują różnicę pomiędzy poszczególnymi operatorami testowania plików. Polecenie stat jest oczywiście najwolniejsze, ponieważ samo z siebie wykonuje najwięcej testów. Poniżej pokazuję wyniki dla zestawu operatorów oraz polecenia stat otrzymane za pomocą modułu Benchmark.
    Wyniki testów Wyniki testów dla 10 milionów wykonań operatorów testowania plików oraz ich porównanie.
    Wyniki w tabeli są przez moduł Benchmark sortowane od przebiegów najwolniejszych, do najszybszych, wyrażonych jako wartość procentowa różnicy pomiędzy każdą parą testów. Z wyników widać, że test -w jest np. o 28% szybszy niż test -d. I tak dalej.
  5. Operatory bitowe mogą być zaskoczeniem, jeżeli dokładnie nie rozumie się, co wykonują. Porównaj działanie następujących przykładów i wyjaśnij co tak naprawdę się dzieje: perl -le 'print "123.45" & "234.56";' perl -le 'print "123.45" & 234.56;'
    Wynikiem w pierwszym przykładzie jest 020.44, natomiast w drugim przypadku jest to liczba 106. Przyczna takiego zachowania leży w tym, jakiego typu są operandy bitowego and. Jeżeli oba z nich to teksty, operator wykonuje bitowe AND na odpowiadających sobie znakach napisu. Jeżeli jednak jeden lub oba operandy są liczbą, zostaje przeprowadzona konwersja na liczbę pozostałych operandów tekstowych, co w ogólnym przypadku nie musi się udać. Tutaj, w drugim przykładzie, prowadzi to wyrażenia 123.45 & 234.56. Operator bitowy nie może jednak działać na liczbach rzeczywistych, dlatego oba operandy są następnie konwertowane do liczb całkowitych i wykonywane jest 123 & 234. Wynikiem takiej operacji jest liczba 106. Bitowo wygląda to tak: 01111011 & 11101010 = 01101010.
  6. Zastanów się nad działaniem operatora +=. Czy na pewno robi dokładnie to samo, co jego rozwinięta wersja? Oto przykład: $a = 0; # trywialne, prawda? $a = $a + 1; $a += 1; W takim razie spróbuj przewidzieć, co będzie wynikiem działania instrukcji: $t[$i++] = $t[$i++] + 1; # i skrócony "odpowiednik" $t[$i++] += 1; Jak myślisz, ile razy wykonuje się w drugim przypadku operacja inkrementacji? Czy można powiedzieć zatem, że operator += rozwija się w pełni tak samo jak =...+... ?
    W operacji += istnieje pewien ukryty mechanizm, który nie spowoduje powtórzenia pokazanego w przykładzie powyżej. Oznacza to, że operacja inkrementacji zmiennej $i wykona się tylko raz, co jest prawidłowe z punktu widzenia logiki programu oraz zapisu. Zatem nie daj się zmylić tłumaczeniu, że $a+=1 to w rzeczywistości $a = $a + 1;. Nie jest tak. :) W ten sposób działają wszystkie operatory przypisania, i zauważ, że mają ten sam priorytet - równy operatorowi przypisania (nie operacji, która jest wykonywana, typu potęgowanie lub katenacja, itp.).
Uniwersytet Gdański - Instytut Matematyki - Zakład Informatyki - Strona domowa - Perl - Zadania
[c] Piotr Arłukowicz, materiały z tej strony udostępnione są na licencji GNU.