I.
Wprowadzenie do  języków zapytań (1)
II.
Wprowadzenie do  języków zapytań (2)
III.
Pojęcia obiektowości w bazach danych (1)
IV.
Pojęcia obiektowości w bazach danych (2)
V.
Podstawy semantyczne języków zapytań
VI.
Modele składu obiektów
VII.
Stos środowisk, rezultaty zapytań, funkcja nested
VIII.
Język SBQL (Stack-Based Query Language) (1)
IX.
X.
Dalsze własności SBQL
XI.
Operatory order by i group by
XII.
Przetwarzanie struktur nieregularnych
XIII.
Rozszerzenie języków zapytań o konstrukcje imperatywne
XIV.
Procedury, procedury funkcyjne, metody, reguły zakresu
XV.
Parametry procedur i metod, procedury rekurencyjne, optymalizacja poprzez modyfikację zapytań
  Wstęp
  1. Parametry procedur, funkcji i metod
  2. Procedury rekurencyjne
  3. Optymalizacja poprzez modyfikację zapytań
  Podsumowanie
  Zadania

 

1. Parametry procedur, funkcji i metod


1.1. Wołanie przez wartość (call-by-value)

Istnieją dwa warianty tej metody, jedna znana z Pascala, druga znana z C. W języku Pascal wewnątrz ciała procedury jej parametr jest innym bytem niż zmienna programistyczna, w szczególności, nie wolno na niego podstawiać. Traktowany jest więc tak jak stała. W języku C wewnątrz ciała procedury parametr ma dokładnie taką samą semantykę, jak lokalna zmienna, tj. można na niego podstawiać, utworzyć do niego pointer itd. Obydwie metody można opisać i zaimplementować w ramach podejścia stosowego. Przyjmijmy składnię deklaracji procedury w postaci:

procedure NazwaProcedury( ...; in NazwaParam; ...){...ciało...}

oraz składnię wywołania:

NazwaProcedury( ...; zapytanie; ...)

Niech powyższe zapytanie zwróci bag{ v1, v2, ... }, gdzie v1, v2, ... Î Rezultat

W wariancie Pascala po wywołaniu procedury zapis aktywacyjny procedury/metody (na wierzchołku ENVS) będzie zawierał bindery:

..., NazwaParam( deref(v1) ), NazwaParam( deref(v2) ), ...

Dzięki temu wewnątrz ciała procedury do parametru można będzie odwołać się poprzez NazwaParam, zaś wynikiem tego odwołania będzie bag{deref(v1), deref(v2), ... }. Funkcja deref dokonuje dereferencji, jeżeli jej argumentem jest referencja, w przeciwnym przypadku zwraca swój argument (nic nie robi).

Wewnątrz ciała nie można dokonać podstawienia na NazwaParam, gdyż wiązanie nazwy NazwaParam nie zwraca referencji. Nazwą tą jest opatrzony bezpośrednio binder znajdujący się na stosie ENVS, ale w składzie taki obiekt się nie pojawia.

Wariant C polega na utworzeniu takiego obiektu, a następnie bindera. Po wywołaniu procedury następuje utworzenie obiektów lokalnych:

<ip1, NazwaParam, deref(v1)>, < ip2, NazwaParam, deref(v2)>,...

gdzie ip1, ip2,... są identyfikatorami nowo utworzonych lokalnych obiektów. Zapis aktywacyjny procedury/metody (na wierzchołku ENVS) będzie zawierał bindery:

..., NazwaParam(ip1), NazwaParam(ip2), ...

Dzięki temu wewnątrz ciała procedury do parametru można będzie odwołać się poprzez NazwaParam (jak poprzednio), ale wynikiem tego odwołania będzie bag{ip1, ip2, ...}. Wewnątrz ciała można zatem dokonać podstawienia na NazwaParam , gdyż są to zwyczajne obiekty lokalne. Obiekty te są automatycznie usuwane po zakończeniu działania procedury.

Jeżeli powyższe zapytanie zwróci nie bag, lecz pojedynczą wartość v, wówczas w wariancie języka Pascal tworzy się analogicznie jeden binder NazwaParam( deref(v) ), natomiast w wariancie języka C jeden obiekt lokalny
<i, NazwaParam, deref(v)> i jeden binder NazwaParam( i ).

P15.1.

Parametrem procedury jest kolekcja nazwisk, wynikiem jest kolekcja par, w których pierwszym elementem jest referencja do pracownika posiadającego nazwisko znajdujące się w kolekcji będącej parametrem, drugim zaś jest referencja do jego kierownika.

   procedure PracSzef( in Naz ) {
        return (Prac where (Nazwisko in Naz))
                join (PracujeW.Dział.Szef.Prac)}

Wywołanie procedury: PracSzef( bag( "Kowal", "Nowak" ) )



 P.15.2.

 

W wariancie Pascala, po wywołaniu procedury na wierzchołku ENVS umieszczona będzie sekcja zawierająca bindery:

Naz("Kowal"), Naz("Nowak")

Wiązanie nazwy Naz występującej w ciele procedury zwróci bag{"Kowal", "Nowak" }. W wariancie C, po wywołaniu procedury utworzone będą lokalne obiekty:

< ip1, Naz, "Kowal" >, < ip2, Naz, "Nowak" >

zaś sekcja na wierzchołku ENVS będzie zawierać bindery Naz(ip1), Naz(ip2).

Wiązanie nazwy Naz występującej w ciele procedury zwróci bag{ ip1, ip2 }. Po zakończeniu procedury obiekty < ip1, Naz,"Kowal">, < ip2, Naz,"Nowak"> zostaną automatycznie usunięte.

 

1.2. Wołanie przez referencję (call-by-reference)

Wołanie przez referencję nie różni się w szczególny sposób od wołania przez wartość w wariancie Pascala. Występują dwie różnice.  Pierwszą z nich jest składnia: ten rodzaj wołania wymaga specjalnego słowa kluczowego (var w Pascalu); w naszym przypadku wybierzemy słowo inout dla podkreślenia, że ten parametr służy jako wejście i wyjście. Zatem składnia deklaracji procedury z takim parametrem będzie następująca:

procedure NazwaProcedury( ...; inout NazwaParam; ...){...ciało...}

oraz składnia wywołania, jak poprzednio:

NazwaProcedury( ...; zapytanie; ...)

W tym przypadku zapytanie musi zwrócić pojedynczą referencję i lub bag referencji bag{i1, i2, ...}. Można zróżnicować składnię w ten sposób, że parametr wołany przez referencję bez możliwości jego odczytu wewnątrz ciała procedury będzie oznaczany słowem out.  Składnię tę pozostawiamy gustowi projektanta języka zapytań/programowania.

Drugą różnicą jest to, że nie jest wykonywana automatyczna dereferencja. Zatem, jeżeli powyższe zapytanie zwróci referencję i, to do zapisu aktywacyjnego wstawiany jest binder NazwaParam( i ), zaś jeżeli powyższe zapytanie zwróci bag{i1, i2, ...}, to do zapisu aktywacyjnego wstawiane są bindery NazwaParam(i1), NazwaParam(i2), ... . To powoduje, że wewnątrz ciała procedury podstawienie na NazwaParam spowoduje podstawienie na referencję (referencje) zakomunikowaną (zakomunikowane) jako parametr. Analogicznie z operacjami usuwania i wstawiania.ten sposób można dokonywać operacji aktualizacyjnych z wnętrza procedury na dowolnych obiektach z jej zewnętrza.

P15.3.

Parametrem procedury jest kolekcja referencji do pracowników, wynikiem jest podwyższenie ich uposażenia o zadaną wielkość.

      procedure Podwyżka ( inout dlaPrac; in Ile ) {
            for each dlaPrac do Zar := Zar + Ile }

Wywołanie procedury:

Podwyżka( Prac where Stan = "analityk"; 200 )

Jeżeli zapytanie Prac where Stan = " analityk"  zwróci bag{i11, i13, i55}, to po wywołaniu procedury na wierzchołku ENVS będzie umieszczona sekcja z binderami: dlaPrac(i11), dlaPrac(i13), dlaPrac(i55), Ile(202).


1.3. Ścisłe wołanie przez wartość (strict-call-by-value)

Zróżnicowanie na wołanie przez wartość i wołanie przez referencję nie zawsze jest korzystne. Wymaga odrębnej składni oraz powoduje ograniczenia, jeżeli chodzi o rodzaj komunikowanej wartości. W języku C takie zróżnicowanie nie występuje: parametr pointerowy (tj. referencja w naszej terminologii) jest przekazywany do ciała procedury bez zmian. W systemie Loqis zdecydowaliśmy się nie wprowadzać omówionych wyżej dwóch odmian komunikowania parametrów. Wzorowaliśmy się na C, ale wprowadziliśmy tę metodę w wariancie Pascala, tj. bez tworzenia lokalnego obiektu. Składnia deklaracji procedury z takim parametrem będzie następująca:

procedure NazwaProcedury( ...; NazwaParam; ...){...ciało...}

oraz składnia wywołania, jak poprzednio:

NazwaProcedury( ...; zapytanie; ...)

Powyższe zapytanie może zwrócić dowolny rezultat r Î Rezultat zbudowany z referencji, wartości, nazw i konstruktorów struktur i kolekcji. Rezultat ten jest bez zmian przekazywany do ciała procedury w ten sposób, że do jej zapisu aktywacyjnego wstawia się pojedynczy binder NazwaParam( r ). W ten sposób metoda ta łączy wołanie przez wartość z wołaniem przez referencję oraz posiada dalsze możliwości, niedostępne w tych metodach.

P15.4.

 

Patrz Rys.61. Parametrem komuIle procedury ZmieńZarobek jest bag struktur struct{ komu(r), ile(w) }, gdzie r jest referencją do obiektu pracownika, w jest jego nowym zarobkiem.

Procedura przyznaje ten nowy zarobek tym pracownikom, dla których jest on większy od ich aktualnego zarobku. W razie konfliktu (jeżeli referencja danego pracownika wystąpi wielokrotnie), wybiera maksymalną z możliwych nowych wartości zarobku. Procedura nie uwzględnia opcyjności atrybutu Zar.

procedure ZmieńZarobek ( komuIle ) {
     for each distinct( komuIle.komu ) as p do
          p.Zar := max( bag( p.Zar, (komuIle where komu = p). ile))}

Wywołanie: Pracownikom z Radomia udziel podwyżki w wysokości 100, pracownikom po 40-tce udziel podwyżki w wysokości 200, zaś wszystkim sekretarkom ustal zarobek na 1000. W razie konfliktu (np. 50-letnich sekretarek z Radomia) wybierz największą z możliwych nowych wartości nowych zarobków.

ZmieńZarobek ( bag(
     ((Prac where "Radom" Î PracujeW.Dział.Lokacja) as komu
                                                join (komu.zar + 100) as ile),
     ((Prac where Wiek > 40) as komu  join (komu.zar + 200) as ile),
     ((Prac where Stan = "sekretarka") as komu, 1000 as ile)))


1.4. Wołanie z etykietowanymi parametrami aktualnymi

Dotychczasowe metody wołania parametrów są niewygodne w sytuacji, gdy parametrów jest więcej niż 5. W takich sytuacjach dokładne dopasowanie kolejności parametrów formalnych i aktualnych oraz ich ilości jest zwykle problemem. Przykładowo, jeżeli deklaracja procedury ma postać:

P15.5.

procedure Wyświetl( tekst; okno; x; y; tło; kolorLiter; font; wys; maxroz; miganie)

to wywołanie tej procedury w postaci:

P15.6.

Wyświetl( kom; A[i]; z+35; a+78; t; 34; f; 12; z/4; 0)

jest bardzo trudne do odczytania i zrozumienia w tekście programu, szczególnie, jeżeli deklaracja tej procedury znajduje się w odległym miejscu kodu. Czytelność mogłyby poprawić komentarze, ale z doświadczeń autora, są one w takich sytuacjach rzadko stosowane przez programistów.

Rozwiązaniem tego problemu jest odmiana techniki wołania parametrów, w której w wywołaniu parametr aktualny jest zawsze skojarzony z nazwą parametru formalnego. To pozwala na podawanie parametrów w dowolnej kolejności. Dodatkowo, technika ta pozwala programiście na pomijanie pewnych parametrów poprzez skorzystanie z wartości domyślnych.

Metodę tę można zastosować w dowolnym wariancie, wołanie przez wartość, przez referencję itd. Niżej podamy odmianę tej metody dla omawianego poprzednio ścisłego wołania przez wartość.

Przyjmiemy, że deklaracja procedury będzie mieć postać, jak poprzednio, natomiast wywołanie procedury będzie zawsze miało postać:

NazwaProcedury( ...; NazwaParam: zapytanie; ...)

W wyniku, jak poprzednio, do zapisu aktywacyjnego procedury będzie wstawiony binder NazwaParam( r ), gdzie r jest rezultatem zwróconym przez zapytanie. W ten sposób kolejność parametrów aktualnych nie będzie miała znaczenia.

Aby można było pomijać niektóre parametry, konieczne są wartości domyślne (default). Istnieją co najmniej dwie metody komunikowania wartości domyślnych:

  • Wartości domyślne są umieszczone wewnątrz klasy. Dotyczy to procedur będących metodami.
  • Wartości domyślne są umieszczone w specjalnej klauzuli procedury nazwanej defaults. Bindery wynikające z tej klauzuli, naśladującej dokładnie wołanie parametrów, są umieszczane na stosie ENVS poniżej zapisu aktywacyjnego.
P15.7.

procedure Wyświetl( tekst; okno; x; y; tło; kolorLiter; font; wys; maxroz; miganie) {
defaults (x:0;y:0;tło:t; font:f;wys:12; maxroz:45; migotanie:0); ... }

Procedura może być wywołana w postaci:

    Wyświetl(okno:A[i]; tekst:"Podaj swoje nazwisko"; migotanie:1)

Pozostałe parametry zostaną pobrane z listy parametrów domyślnych lub z wartości domyślnych umieszczonych w ramach klasy, do której należy procedura Wyświetl.


Copyrights © 2006 PJWSTK
Materiały zostały opracowane w PJWSTK w projekcie współfinansowanym ze środków EFS.