Przekazywanie parametrów

Cele:

Zaczynamy od przedstawienia problemów i metod przekazywania parametrów w Loglanie.
 

LOGLAN

Każdy moduł może mieć parametry formalne.
Parametry formalne mogą być:

Parametr formalny - zmienna deklarowanego modułu
 - trzy sposoby przekazywania parametru aktualnego

Założylismy przy tym, że zmienne a, boo i zmienna1 są zadeklarowane w miejscu wystąpienia instrukcji procedury.
Złożylismy też, że wsród wartosci lokalnych procedury metoda nie występują  wartosci o takich nazwach.
W przeciwnym przypadku znaczenie instrukcji albo jest nieokreslone (błąd braku deklaracji) albo trzeba je wyjasnić używając nieco dłuższego opisu.
 

 
PRZYPOMNIJMY ,

regułę kopii (ang. copy rule)
 

 instrukcja procedury

   jest równoważna zmodyfikowanej tresci procedury, poprzedzonej przekazaniem 
   parametrów input i zakończonej przekazaniem parametrów output

  call zamien(x, y)

      a := x; b := y;            bsp;                         // przekazanie parametrów input 
      aux := a; a := b; b := aux;        // tresć procedury 
      x := a; y := b;                          // przekazanie parametrów output

 


Przekazywanie podprogramu (procedury lub funkcji)

Parametrem formalnym procedury może być nazwa procedury lub funkcji

Kompilator może wykonać analizę zgodnosci typów, ponieważ w nagłówku funkcji bisekcja umiescilismy założenie, że drugi parametr funkcji bisekcja ma być funkcją 1 argumentowa, argument ma być wartoscią typu real  a wynik funkcji f też ma być typu real. (Kompilator wymaga by jakies założenie o liczbie i typach argumentów a także o typie wyniku zostało napisane w sposób jawny)

Funkcja bisekcja jest przykładem funkcji wielokształtnej (ang. polymorphic).
 

Funkcje i procedury wielokształtne odgrywają ważną role w inżynierii oprogramowania. Dzięki nim można wielokrotnie wykorzystać raz zaprogramowany algorytm.



Przekazywanie typu

W Loglanie dopuszczalne jest przekazywanie nazwy typu.

    Przykład
    unit swap: procedure(type T, inout x, y: T);
       var aux: T;
    begin
       aux := x; x := y; y := aux
    end swap;

Przykład ten jest czytelny i nie wymaga objasnień.
W przykładzie tym występują wszytkie możliwosci wykorzystywania parametru formalngo, który jest nazwą typu.
Wspomnijmy najpierw, że parametrem aktualnym może być tylko nazwa klasy(!) a nie może być nazwa typu pierwotnego (nie jest to ograniczenie zbyt dolegliwe).

Warto zwrócic uwagę na to, że

  1. procedura swap zastępuje nieskończoniqua, serif">procedura swap zastępuje nieskończoną kolekcję procedur: zamienInteger, zamienreal, zamienChar, zamienTypT1, zamienTypT2, ... (Dla każdego typu można wyobrazić sobie odpowiednią procedurę zamien),

  2. kompilator jest w stanie sprawdzić poprawnosć samej procedury swap jak i jej zasosowań.
    Instrukcja call(tp, u, v) jest poprawna gdy u i v są zmiennymi typu tp.

Zastosowania parametru formalnego, który jest nazwą typu w danym module są ograniczone do następującej listy:

  • parametrem formalnym modułu może być nazwa klasy,

  • nazwa ta może być uzyta dla okreslenia typu deklarowanej zmiennej,

  • nazwa ta (w niejawny sposób) może być wykorzystywana dla sprawdzenia poprawnosci instrukcji przypisania badź transmisji parametrów.

Nic więcej nie można z przekazanej nam nazwy typu "wycisnąć". Ale jest to nie mało.

Popatrzmy na następujący

    PrzykładP>

      Przykład
      unit sortuj: procedure(type T,function less(x,y: T): Boolean, A: arrayof T);
          (* Jeżeli przekażemy procedurze sortuj: nazwę pewnej klasy T, funkcję
          (* logiczna obliczającą odpowiedź na pytanie czy dla dwu danych obiektów
          (* klasy T, obiekt x jest mniejszy od obiektu y oraz tablicę obiektów typu T
          (* to procedura sortuj uporządkuje elementy tablicy A *)
          
      var
      begin
         if A = none then return fi;
       
      end sortuj;

    W przykładzie tym zastosowano zasadę Jenssena (ok. 1960), która mówi gdy nie da się przekazać informacji w jednym tylko parametrze aktualnym to przekaż więcej parametrów, dbając o to by były one powiązane pomiędzy sobą. Dzis powiedzielibysmy, że dane czyli parametry aktualne procedury sortuj mają spełniać następujący warunek wstępny alfa(T, less, A):

    1. pierwszy parametr T ma być nazwą klasy,

    2. drugi parametr less ma być nazwą funkcji boolowskiej o dwu parametrach typu T, funkcja ta ma zwracać odpowiedź na pytanie czy pierwszy argument jest mniejszy od drugiego,

    3. trzeci argument A ma być tablicą obiektów typu T.

    Nagłówek procedury sortuj zawiera częsć warunku wstępnego, kompilator sprawdza czy trzeci parametr aktualny jest tablicą obiektów typu wymienionego jako pierwszy prametr aktualny i czy argumenty funkcji przekazanej jako drugi parametr są tego typu. Nagłowek nie mówi jednak o tym,etr są tego typu. Nagłowek nie mówi jednak o tym, że funkcja less ma spełniać własnosci relacji porządku: ma być zwrotna, przechodnia, antysymetryczna i spójna. I niewiele tu pomoże próba dynamicznej  weryfikacji warunku wstępnego na początku działania algorytmu sortuj. Własnosci takich jak przechodniosć, antysymetria nie da się sprawdzic, trzeba je udowodnić.

    Załóżmy optymistycznie, że udowodnilismy iż funkcja less ma pożądane własnosci. Przy tym założeniu możemy wykazać, że algorytm sortuj da wynik zgodny z warunkiem końcowym.
    Zauważmy, że procedura sortuj podobnie jak poprzednio cytowana procedura swap są procedurami wielokształtnymi (ang. polymorphic). Przez wielokształtnosć rozumiemy własnosć, która pozwala stosować procedurę, lub inny moduł, do danych różnych typów. Procedura zamien nie jest wielokształtna. Realizuje ona zamiane wartosci dwu zmiennych typu real. Nie może być zastosowana do zamiany wartosci zmiennych typu integer ani character.


    Abstrakcyjne typy danych z parametrami

    Omówić stosy lub kolejki elementów typu ... DWA ROZWIĄZANIA:
    - z typem element jako parametrem
         &nbm
           unit StosyP: class(type element);
           end StosyP;

     
    - z dziedziczeniem i zagnieżdżaniem
        unit Stosy: class;
             unit element: class;
             unit stos: class;
        end Stosy

       unit MojeStosy: Stosy class;
            unit MojElement: element class
       end MojeStosy; MojeStosy;

    Omówić typy danych z virtualnymi metodami: B-drzewa,

    Omówić
     



     



     

    Niebezpieczeństwa:

    Wyobraźmy sobie, że w  przykładzie bisekcja ominięto informację o liczbie i o typach argumentów funkcji f i że kompilator to zaakceptował (tak robi wiele kompilatorów! ale nie kompilatory LOGLANu)

      unit bisekcjaNiepewna: function(in a, b: real, function f, in epsi: real): real;
         (* o funkcji f nie wiemy nic *)
         var c: real;

      begin
      while abs(b - a) > epsi
      do
         c := (a + b)/2;
         if f(c) * f(a) < 0      (* kompilator nie ma danych by zweryfikować ten sposób użycia f    *)
                                 (* kompilator nie mógłby odrzucić frazy:      if f(c) AND f(b,c) *)
         then
            p;   
      b := c
         else
            a := c
         fi
      od;
      result := a;
      end bisekcjaNiepewna
       

    Przy takiej deklaracji funkcji bisekcjaNiepewna kompilator nie jest w stanie wskazać, że instrukcja
    x := bisekcjaNiepewna(1, 2, g, 0.001); jest niepoprawna bo np. funkcja g ma dwa parametry,
    y := bisekcjaNiepewna(1, 2, h, 0.0001); jest niepoprawna bo np. typ funkcji h różni się od typu real.
     


    Niedogodnosci:

    Jeżeli język programowania rygorystycznie sprawdza czy typy są zgodne to procedura zamien musi być zadeklarowana dla każdego z typów do jakich ma się odnosić (Język pozwala okreslić nieograniczoną liczbę typów).



     

    Funkcje i procedury wzorcowe (ADA, C++, (?)Java)

    Z powodu zagrożeń dla bezpieczeństwa o jakich dyskutowalismy powyżej język ADA zabrania przekazywania funkcji i procedur jako parametrów. Ponieważ jednak korzysci płynące z użycia nazw procedur i funkcji jako parametrów są bardzo duże, wprowadzono funkcje i procedury wzorcowe (w ADZIE ang. generic, w C++ ang template).

    ADA

    Parametrami formalnymi modułu w ADZIE mogą być zmienne. Parametry aktualne oznacznika funkcyjnego mogą być przekazywane przez wartosć (in), dla wyniku (out), przez wartosći dla wyniku (in out), czyli podobnie jak w Loglanie.

    Procedura wzorcowa ma postać jak następuje

      Przykład
       

      generic
         type Element is private;
      procedure Zamien(X, Y: in out Element);

      procedure Zamien(X, Y: in out Element) is
          T: Element
      begin
          T := X; X := Y; Y := T;
      end;

    Podprogram wzorcowy, tu Zamien jest koniecznie deklarowany w dwu częsciach:

    • specyfikacja podprogramu jest poprzedzona listą parametrów formalnych wzorcowych na którą składają się słowo kluczowe generic za którym znajduje się lista (być może pusta) i nagłówek procedury.

    • implementacja  podprogramu jest napisana jak zwykle.

    W przypadku podprogramów wzorcowych jestesmy zobowiązani do napisania i specyfikacji i implementacji.(W przypadku zwykłych podprogramów nie jestesmy do tego zobowiązani).

    Procedura wzorcowa nie może być wzywana bezposrednio, należy stworzyć jej instancję lub instancje.

    Posługując się  procedurą wzorcową Zamien tworzy się instancje. Na przykład,
       
    procedure Zamienic is new ans-serif">procedure Zamienic is new Zamien(Float)

    lub

    procedure ZamianaCmp is new Zamien(complex)
    lub
       procedure Zamienic is new Zamien(Data)

    Nazwa Zamienic może być przeciążana na ogólnych zasadach obowiązujących w języku ADA.

     ============================================

    C++

    Jedynym sposobem przekazywania parametru aktualnego zmiennej - parametrowi formalnemu jet przekazywanie przez wartosć. Jest to jeden z aksjomatów języka C. Cena jaką zapłacono za realizację tego postulatu to stworzenie typów referencyjnych. Konieczne okazało się odróżnienie r-wartosci od l-wartosci. Zalew typów jakie programista może stworzyć w swoim programie okazał się mocnym stymulatorem dla nasladowania procedur i typów "generic" z ADY.

    Z wielu powodów w C++ nie można przekazac nazwy typu jako argumentu funkcji.
    W języku C++ przyswojono ideę podprogramu wzorcowego zaczerpniętą z ADY.
    Mozna  zadeklarować funkcję wzorcową, słowo "generic" zastąpiono słowem "template".
     

      Przykład
      template <class Tp>
      Tp min(Tp a, Tp b)
      {
       if (a < b)
           return a;
       else
           return b;
      }

     Taka funkcja wzorcowa może znaleźć wiele różnych konkretyzacji
     

      Przykład
      int i = min(7, 67)

      double d = min(7.8, 34.9)

    Konkretyzowanie, czyli ustalenie wersji funkcji min() polega na wyznaczeniu konkretnego typu który w danym przypadku należy podstawić jako parametr Tp. Jest on wyznaczony przez typ pierwszego parametru aktualnego.
    Typ zmiennej na którą podstawiana jest obliczona wartosć funkcji nie ma tu żadnego znaczenia!
     
    ============================================

     Java

      W Javie program jest kolekcją klas.

    NT>

      W Javie program jest kolekcją klas.