06 Procedury i funkcje cwiczenia przygotowujace

background image

VI.

Procedury i funkcje – metody

Język Visual Basic 2010 umożliwia programiście utworzenie ciągu instrukcji i nadanie mu na-

zwy. Możliwość ta jest odzwierciedleniem praktyki życiowej polegającej na nadawaniu pewnemu cią-

gowi czynności nazwy, która jednoznacznie opisuje te czynności oraz ich kolejność. Przykładami ta-

kich nazw są: odkurzanie, skakanie ze spadochronem, smażenie jajecznicy itp. Drugi przykład ma tu

szczególne znaczenie, gdyż wiadomo, że pominięcie którejkolwiek z czynności w procedurze skaka-

nia ze spadochronem może zakończyć się tragicznie.

Słowem kluczowym poprzedniego akapitu jest „procedura”. Określa ono wydzielony z programu

ciąg instrukcji, któremu nadano nazwę. Nazwa służy do uruchamiania takiego ciągu instrukcji (czyli

procedury) w dowolnej części programu. Visual Basic 2010 pozwala na tworzenie kilku typów proce-

dur. Bieżący rozdział omawia jedynie dwa z nich – podprogramy i funkcje – określane wspólnym mia-

nem metod.

VI.1.

Podprogramy – metody typu Sub

Załóżmy, że programista chciałby, aby każdy jego program rozpoczynał swoją pracę od przed-

stawienia pewnych danych.

Przykład.

********************************************
* *
* Program wykonał: Imię Nazwisko *
* adres e-mail: konto@serwer *
* *
********************************************

Naciśnij Enter, aby kontynuować...

Napisanie kodu źródłowego, który wykona taką winietę nie nastręcza żadnych kłopotów – wy-

starczy użyć instrukcji WriteLine i ReadLine konsoli. Jednak pisanie takiego kodu, a nawet kopiowa-

nie go wymaga czasu. Kolejne ćwiczenie będzie polegać na napisaniu kodu realizującego przedsta -

wienie danych o autorze projektu.

Ćwiczenie 60
Utworzyć Projekt_060 aplikacji konsolowej, która przedstawi dane autora aplikacji w
sposób podobny do przedstawionego w przykładzie powyżej (użyć własnych danych –
imię, nazwisko i e-mail).

Aplikacja wykonuje winietę przedstawiając dane autora projektu.

background image

Aby uniknąć ciągłego przepisywania instrukcji kodu w kolejnym ćwiczeniu utworzony zostanie

podprogram – procedura typu Sub, której podstawowa postać wygląda następująco:

Sub NazwaPodprogramu()

‘Instrukcje

End Sub

Ćwiczenie 61
Dodać do Projekt_060 moduł o nazwie Moje_060 i zdefiniować w nim procedurę Sub o
nazwie Winieta, w której bloku znajdą się instrukcje przeniesione z procedury Main.

1. Dodaj do Projekt_060 moduł Moje_060 (wykorzystaj schemat wprowadzony w ćwiczeniu

18.).

2. Utwórz w dodanym module szkielet procedury Winieta, w tym celu:

wpisz słowo kluczowe Sub, rozpoczynające deklarację podprogramu i dopisz spa-

cję,

wpisz nazwę procedury – Winieta i naciśnij Enter. Zauważ, że edytor wspomaga

tworzenie procedur, dodając po nazwie parę nawiasów okrągłych oraz słowa klu-

czowe End Sub kończące blok podprogramu. Kursor tekstowy znajduje się we-

wnątrz bloku.

3. Przenieś wszystkie instrukcje z procedury Main do procedury Winieta.

4. Sprawdź działanie aplikacji.

Aplikacja nie wykonuje winiety, gdyż podprogram nie został uruchomiony. Istnieją dwa sposoby

uruchamiania podprogramów. Pierwszy, tradycyjny, polega na użyciu słowa kluczowego Call (Wołaj,

przywołaj), a po nim nazwy podprogramu.

Przykład.

Call Podprogram()

Druga, najczęściej współcześnie stosowana, polega na użyciu nazwy podprogramu tak, jak

zwykłej instrukcji języka.

Przykład.

Podprogram()

Kolejne ćwiczenie polegać będzie na wywołaniu podprogramu Winieta w procedurze Main.

Ćwiczenie 62
Uruchomić podprogram Winieta w procedurze Main.

1. Wpisz w procedurze Main instrukcję Winieta().

background image

2. Sprawdź działanie aplikacji.

Każdy projekt, do którego programista dołączy moduł Moje_060 będzie mógł wykorzystać pro-

cedurę Winieta. Obrazuje to kolejne ćwiczenie.

Ćwiczenie 63
Utworzyć Projekt_063 aplikacji konsolowej. Dodać do niego moduł Moje_060 i wywołać
w procedurze Main podprogram Winieta. Sprawdzić działanie aplikacji.

1. Utwórz nowy projekt aplikacji konsolowej.

2. Dodaj do projektu moduł Moje_060, w tym celu:

wykorzystaj polecenie Add Existing Item (Dodaj istniejący element) zawarte w

menu Project (polecenie dostępne także w grupie poleceń Add menu podręczne-

go projektu w oknie Solution Explorer); polecenie posiada skrót klawiaturowy –

Ctrl+D; otwiera ono okno Add Existing Item będące kopią systemowego okna

Otwieranie,

odszukaj folder Projekt_060, a w nim plik modułu Moje_060 i zaznacz go,

zatwierdź wybór przyciskiem Add.

3. Zauważ, że w oknie Solution Explorer pojawił się plik Moje_060.vb (jest to kopia pliku za -

wartego w Projekt_060).

4. Sprawdź kod źródłowy modułu Moje_060.

5. Wywołaj w procedurze Main procedurę Winieta.

6. Sprawdź działanie aplikacji.

Aplikacja Projekt_063 „przedstawia się” identycznie jak aplikacja Projekt_060. Tworzenie kopii

pliku pozwala programistom edytować moduły niezależnie.

Jeśli programista otrzyma nowy adres e-mail (np. po zmianie miejsca pracy) , to będzie zmu -

szony do edycji procedury Winieta. Podobnie będzie przy zmianie nazwiska (współcześnie, zjawi-

sko to, dotyczy tak kobiet, jak i mężczyzn). Problem ten rozwiązuje się poprzez zdefiniowanie para-

metrów podprogramu.

Parametry pozwalają na przekazanie do podprogramu wartości wpływających na sposób wyko-

nania podprogramu. Rzeczywistym przykładem może być obliczanie kwadratu liczby. Wiadomo, że

aby obliczyć kwadrat liczby, należy pomnożyć ją przez taką samą wartość. Przykładowo, 2

2

=2*2.

W matematyce zapis ten uogólnia się podając wzór: x

2

=x*x. We wzorze tym, x jest parametrem (ar-

gumentem) funkcji kwadratowej. Znając ten wzór można obliczyć kwadrat dowolnej liczby.

background image

Parametry procedury

Miejscem definiowania parametrów jest wnętrze pary nawiasów okrągłych występujących

po nazwie podprogramu. Parametry pozwalają przekazywać do podprogramu pewne dane w chwili

jego wywołania. Podprogram, któremu przekazano dane poprzez parametry wykorzystuje je w trakcie

działania. Istnieją dwa sposoby przekazywania danych do podprogramu:

przez wartość (ang. by value),

przez referencję (ang. by reference).

Objaśnienie różnicy pomiędzy tymi sposobami nastąpi w podpunkcie dotyczącym parametrów

przekazywanych przez referencję, do tego czasu wykorzystywany będzie jedynie pierwszy z nich.

Parametry przekazywane przez wartość

Deklaracja parametru przekazywanego przez wartość wygląda następująco:

ByVal NazwaParametru As TypParametru

Słowo kluczowe

ByVal

(skrót od by value) można opuścić, gdyż jest to domyślny sposób prze-

kazywania parametrów i edytor doda je automatycznie. Jeśli programista definiuje więcej niż jeden

parametr, to deklaracje rozdziela się przecinkiem.

Przykład.

ByVal NazwaParametru1 As TypParametru1, ByVal NazwaParametru2 As TypParametru2

Kolejne ćwiczenie będzie polegać na zdefiniowaniu dwóch parametrów w procedurze Winieta,

posłużą one do przekazywania do procedury nazwiska i adresu e-mail.

Ćwiczenie 64
Zdefiniować w procedurze Winieta parametry Nazwisko i Email typu String
przekazywane przez wartość.

1. Umieść kursor tekstowy wewnątrz pary nawiasów występujących po słowie Winieta w

module Moje_060.

2. Zdefiniuj parametr Nazwisko typu String przekazywany przez wartość, w tym celu:

wpisz słowo ByVal i dopisz spację,

wpisz nazwę parametru – Nazwisko i dopisz spację,

określ typ parametru – As String.

Przykład.

ByVal Nazwisko As String

background image

3. Zdefiniuj drugi parametr – Email typu String, w tym celu:

wpisz przecinek po słowie String określającym typ parametru Nazwisko i dopisz spa-

cję,

nie wpisuj słowa kluczowego ByVal – automatycznie doda je edytor,

wpisz nazwę parametru – Email – dopisz spację i określ typ parametru.

4. Przenieś kursor tekstowy do innego miejsca kodu i zauważ, że słowo kluczowe ByVal zo-

stało dodane przed deklaracją parametru Email.

5. Spróbuj uruchomić aplikację.

6. Zamknij przyciskiem Nie (No) komunikat o błędach w trakcie kompilacji.

Wywołanie procedury musi spełniać wymagania postawione w jej deklaracji. Procedura Wini-

eta posiada dwa parametry i tyle musi również zawierać jej wywołanie. Edytor jest w tym zakresie

niezwykle pomocny – w trakcie wpisywania do kodu źródłowego wywołania procedury „podpowiada”

jakich wymaga ona parametrów. Obrazuje to kolejne ćwiczenie.

Ćwiczenie 65
Wprowadzić wartości parametrów do wywołania procedury Winieta.

1. Ustaw kursor tekstowy wewnątrz pary nawiasów okrągłych w wywołaniu podprogramu

Winieta w procedurze Main.

2. Naciśnij klawisz Delete (Usuń) i zauważ, że edytor wyświetla informację o wymaganych

parametrach podprogramu podając ich nazwy i typy. Jest to sytuacja analogiczna do tej,

jaka nastąpiłaby podczas wprowadzania nazwy podprogramu po wprowadzeniu lewego

nawiasu okrągłego.

Przykład.

3. Wpisz w cudzysłowie ciąg znaków "NoweNazwisko" – będzie to wartość parametru Na-

zwisko przekazana do podprogramu.

4. Wpisz przecinek, a po nim w cudzysłowie ciąg znaków "Nowy E-mail" – będzie to war-

tość parametru Email przekazana do podprogramu.

5. Sprawdź działanie aplikacji.

background image

Projekt został skompilowany, a aplikacja uruchomiona. Jednak w winiecie nie pojawiły się tek-

sty: NoweNazwisko i Nowy E-mail. Podprogram wprawdzie otrzymał poprzez parametry wspomniane

łańcuchy znaków, ale z nich nie korzysta. Przed wykonaniem kolejnego ćwiczenia, które ten błąd na-

prawi należy powiedzieć, że z parametrów podprogramu można (wewnątrz bloku podprogramu) ko -

rzystać tak, jak ze zmiennych.

Ćwiczenie 66
Użyć wartości parametrów Nazwisko i Email w podprogramie Winieta.

1. Zmodyfikuj wywołanie procedury WriteLine wypisującej wiersz z nazwiskiem tak, aby wy-

korzystywała wartość parametru Nazwisko.

Przykład. Procedurę Lset (patrz: Funkcje działające na łańcuchach znaków, podrozdział II.9)

użyto aby uwzględnić fakt, że nazwiska mogą mieć różną długość, co nie powinno mieć wpływu na

wygląd ramki z gwiazdek.

Console.WriteLine("* Program wykonał: Imię {0}*", LSet(Nazwisko, 15))

2. Zmodyfikuj wywołanie procedury WriteLine wypisującej wiersz z adresem e-mail tak, aby

wykorzystywała wartość parametru Email (weź pod uwagę zmienną długość adresu).

3. Sprawdź działanie aplikacji.

Prześledźmy działanie programu. Aplikacja, po wywołaniu procedury Winieta, tworzy dla niej

zmienne typu

String

o nazwach Nazwisko i Email, następnie oblicza wartości parametrów, które

w ogólności mogą być wyrażeniami, i przypisuje wyniki do utworzonych zmiennych. Kolejnym kro-

kiem jest wykonanie instrukcji podprogramu. Ponieważ zmienne posiadają wartości pobrane z ze -

wnątrz, to każde wywołanie podprogramu może dać inny efekt w zależności od wartości parametrów

podanych w wywołaniu. Parametry podprogramu „żyją” tylko tak długo, jak długo wykonywany jest

podprogram. Zmiana wartości parametru wewnątrz podprogramu nie wpływa zatem na zmienne zde-

finiowane na zewnątrz.

Wspomniano, że dane przekazywane przez wartość mogą być wyrażeniami tak, jak w poniż-

szym przykładzie (zakłada się, że zmienne strKonto i strSerwerPoczty są typu String i zawierają od -

powiednie łańcuchy tekstowe):

Winieta("Nowenazwisko", strKonto & "@" & strSerwerPoczty)

Opis działania aplikacji będzie identyczny z poprzednim. Ponownie aplikacja tworzy zmienne

podprogramu – Nazwisko i Email – następnie pobiera wartość pierwszego parametru wywołania i

przypisuje ją pierwszej zmiennej. Kolejnym krokiem jest obliczenie wartości wyrażenia:

strKonto & "@" & strSerwerPoczty

background image

Obliczona wartość – łańcuch tekstowy, np. "konto123@serwer.w.pl" – zostaje przypisana zmien-

nej podprogramu o nazwie Email. Obie zmienne mogą być następnie wykorzystane w trakcie działa -

nia podprogramu.

Aby odróżnić parametry określone w deklaracji podprogramu od wartości (wyrażeń) podawa-

nych w instrukcji wywołującej podprogram, te pierwsze nazywa się parametrami formalnymi, a te dru-

gie parametrami aktualnymi.

Należy podkreślić jeszcze jeden ważny problem. Jeśli jako parametr wywołania podprogramu

zostanie podana zmienna (zakłada się, że w procedurze Main zadeklarowano zmienne Nazwisko i

Adres oraz nadano im odpowiednie wartości):

Winieta(Nazwisko, Adres)

to w dalszym ciągu, aplikacja tworzy nową zmienną Nazwisko (a także Email), która jest zmien-

ną podprogramu i nie jest tożsama ze zmienną Nazwisko zadeklarowaną w procedurze Main. Jedy-

ne co je łączy, to to, że wartość zmiennej Nazwisko z procedury Main zostanie pobrana i przypisana

zmiennej Nazwisko z podprogramu Winieta. Jakakolwiek zmiana wartości zmiennej Nazwisko

w procedurze Winieta nie ma wpływu na wartość zmiennej Nazwisko w procedurze Main.

Zdarza się jednak, że zachodzi potrzeba, aby podprogram zmieniał wartość zmiennej, którą

przekazano mu jako parametr. Zagadnienie to omawia kolejny podpunkt.

Parametry przekazywane przez referencję

Deklaracja parametru przekazywanego przez referencję wygląda następująco:

ByRef NazwaParametru As TypParametru

Słowa kluczowego

ByRef

(skrót od by reference) nie można opuścić, gdyż w takim przypadku

edytor doda automatycznie słowo kluczowe

ByVal

. Angielskie słowo reference oznacza wzmiankę,

odesłanie (np. odsyłacz do literatury), wskazanie.

Jeśli w wywołaniu procedury, w miejscu parametru zdefiniowanego ze słowem

ByRef

, progra-

mista umieści nazwę zmiennej, nazwę pola zmiennej typu strukturalnego, nazwę komórki tablicy, ge-

neralnie – nazwę każdego elementu, którego wartość można modyfikować w programie, to zmiany

wartości parametru wewnątrz podprogramu będą miały wpływ na wartość tego elementu w progra-

mie.

W przypadku, gdy programista umieści wyrażenie, lub nazwę elementu, którego wartości nie

można zmienić w programie, np. stałej, elementu enumeracji itp., możliwe będzie jedynie zmienianie

wartości zmiennej w podprogramie tak, jak to miało miejsce dla parametrów przekazywanych przez

wartość. Kolejne ćwiczenie zapoznaje z definiowaniem parametrów przekazywanych przez referen-

cję.

background image

Ćwiczenie 67
Zdefiniować procedurę PobierzLiczbę o jednym parametrze PobieranaLiczba typu
Integer przekazywanym przez referencję i dwóch parametrach Min i Maks typu Integer
przekazywanych przez wartość. Procedura powinna wyświetlać zachętę do
wprowadzania liczby całkowitej z przedziału <Min, Maks> i nie powinna pozwalać na
wprowadzenie liczby leżącej na zewnątrz tego przedziału (wykorzystać pętlę z
warunkiem sprawdzanym na końcu).

1. Dodaj pusty wiersz w module Moje_060 po słowach End Sub kończących procedurę Wi-

nieta. Treść procedury Winieta można zwinąć używając przełącznika zwiń/rozwiń.

2. Utwórz szkielet deklaracji procedury PobierzLiczbę wpisując słowo kluczowe Sub, po

spacji nazwę procedury i naciskając Enter.

3. Zdefiniuj, wewnątrz pary nawiasów okrągłych dodanych przez edytor po nazwie procedu-

ry, parametr PobieranaLiczba typu Integer przekazywany przez referencję, w tym celu:

wpisz słowo kluczowe ByRef (nie wolno go pominąć) i dopisz spację,

wpisz nazwę parametru – PobieranaLiczba – i określ jego typ.

4. Zdefiniuj parametr Min typu Integer (nie musisz wpisywać słowa kluczowego ByVal).

5. Zdefiniuj parametr Maks typu Integer.

6. Zbuduj wewnątrz procedury pętlę Do … Loop z warunkiem sprawdzanym na końcu, pętla

powinna być wykonywana tak długo, aż wartość zmiennej PobieranaLiczba będzie

w przedziale <Min, Maks>.

7. Zaprogramuj wewnątrz pętli wyświetlanie zachęty do wprowadzenia wartości z przedzia-

łu <Min, Maks> oraz pobieranie od użytkownika wartości i przypisywanie jej zmiennej Po-

bieranaLiczba.

8. Skompiluj projekt.

Zdefiniowana procedura może mieć następującą postać:

Tylko parametr PobieranaLiczba może posłużyć do przekazania wartości na zewnątrz procedu-

ry PobierzLiczbę. Aby użyć tej możliwości, jako pierwszy parametr wywołania musi wystąpić ele-

ment zmienny, np. zmienna programu, komórka tabeli itp. Kolejne ćwiczenie pokaże zmianę wartości

zmiennej programu po wywołaniu procedury z parametrem przekazywanym przez referencję.

Rysunek 24: Jedna z możliwych postaci procedury PobierzLiczbę.

background image

Ćwiczenie 68
Użyć parametru przekazywanego przez referencję do zmiany wartości zmiennej na
zewnątrz procedury.

1. Zadeklaruj w procedurze Main zmienną intLiczba typu Integer o wartości początkowej 9.

2. Wywołaj procedurę PobierzLiczbę z parametrami: intLiczba, 10, 15.

3. Zaprogramuj wyświetlenie w konsoli wartości zmiennej intLiczba.

4. Sprawdź działanie aplikacji.

Aplikacja wyświetla winietę, pobiera liczbę z przedziału <10, 15> i wyświetla wartość zmiennej

intLiczba, która nie wynosi 9. Działanie aplikacji jest odmienne od tego, które występuje dla parame -

trów przekazywanych przez wartość. Zmienna intLiczba zadeklarowana w procedurze Main zajmuje

w pamięci operacyjnej określone miejsce (posiada adres). W chwili wywołania procedury Pobierz-

Liczbę (oprócz zmiennych Min i Max) tworzona jest zmienna PobieranaLiczba. Jest to zmienna re-

ferencyjna. Referuje ona (wskazuje, odwołuje się) do tego samego miejsca w pamięci operacyjnej,

które zostało przydzielone w celu przechowywania wartości zmiennej intLiczba. Występuje zatem

taka sytuacja, że dwie zmienne przechowują swoją wartość w tym samym miejscu pamięci operacyj-

nej. Jeśli wartość zmiennej PobieranaLiczba zmieni się, to jest to jednoznaczne ze zmianą wartości

zmiennej intLiczba.

Procedura PobierzLiczbę służy do wprowadzania liczb z określonego przedziału i jest dosyć

ogólna, gdyż pozwala podać granice przedziału jako parametry wywołania (Min, Maks). Załóżmy, że

procedura ta ma zostać wykorzystana wielokrotnie w programie, i w 95% wywołań górne ogranicze -

nie – Maks – wynosi 215. Jeśli wywołań jest 100, to programista będzie musiał wpisać 95 razy liczbę

215 i 25 razy inną liczbę. Problem ten został rozwiązany przez parametry opcjonalne.

Parametry o wartości domyślnej – opcjonalne

Parametry o wartości domyślnej tak, jak zwykłe parametry służą do przekazywania pewnych

wartości do procedury. Charakteryzują się tym, że jeśli programista nie określi, podczas wywołania

procedury, wartości parametru – pominie go, to procedura nada zmiennej wartość domyślną określo-

ną w deklaracji procedury. W tym zakresie występuje ograniczenie, a mianowicie, parametry domyśl-

ne muszą występować na liście parametrów jako ostatnie. Deklaracja parametru opcjonalnego ma

następującą postać:

Optional ByVal NazwaParametru As TypParametru = WartośćDomyślna

lub

Optional ByRef NazwaParametru As TypParametru = WartośćDomyślna

background image

Jeśli w wywołaniu procedury, w miejscu parametru NazwaParametru nie występuje wartość, to

zmiennej NazwaParametru, w procedurze, zostanie przypisana wartość początkowa WartośćDomyśl-

na. Definiowanie parametru opcjonalnego przedstawia kolejne ćwiczenie.

Ćwiczenie 69
Ustalić wartość domyślną parametru Maks na 215.

1. Ustaw kursor tekstowy przed słowem kluczowym ByVal rozpoczynającym deklarację pa-

rametru Maks i dopisz słowo kluczowe Optional (opcjonalny), następnie ustaw kursor po

określeniu typu parametru i dopisz operator przypisania i liczbę 215.

2. Skompiluj projekt.

Pominięcie parametru w chwili wywołania procedury należy traktować dosłownie. Załóżmy, że

wywołanie procedury PobierzLiczbę ma postać:

PobierzLiczbę(intLiczba, 10, 15)

W wywołaniu tym parametr Maks ma wartość 15, pominięcie, oznacza usunięcie go z wywoła-

nia:

PobierzLiczbę(intLiczba, 10, )

Liczba parametrów opcjonalnych może być większa. W kolejnym ćwiczeniu ustala się wartość

domyślną parametru Min na 0.

Ćwiczenie 70
Ustalić wartość domyślną parametru Min na 0.

Procedura PobierzLiczbę ma już dwa parametry domyślne. Parametry domyślne można po-

mijać pojedynczo, lub wszystkie, lub dowolną ich kombinację. Jeśli programista chce pominąć tylko

parametr Min, a nadać wartość 100 parametrowi Maks, to musi to zrobić następująco:

PobierzLiczbę(intLiczba, , 100)

Przecinki wskazują miejsce pominiętego parametru. Przy tym, jeśli pomija się grupę parame-

trów leżących na końcu listy, to przecinków można nie wpisywać. Np. pomijając ostatni parametr

można użyć wywołania:

PobierzLiczbę(intLiczba, 10)

a pomijając dwa ostatnie:

PobierzLiczbę(intLiczba)

Kolejne ćwiczeniu pokazuje różne wywołania z uwzględnieniem parametrów opcjonalnych.

background image

Ćwiczenie 71
Wykorzystać procedurę PobierzLiczbę i różne kombinacje jej parametrów opcjonalnych.

1. Skopiuj dwa wiersze: wywołanie procedury PobierzLiczbę i instrukcję wypisującą wartość

intLiczba w konsoli.

2. Wklej skopiowane wiersze pięć razy na końcu procedury Main.

3. Usuń liczbę 15 z pierwszego wywołania (pozostaw przecinek po liczbie 10).

4. Usuń liczbę 15 oraz poprzedzający ją przecinek z drugiego wywołania.

5. Usuń liczbę 10 z trzeciego wywołania.

6. Usuń liczby 10 i 15 z czwartego wywołania (pozostaw przecinki).

7. Usuń liczby 10 i 15 oraz przecinki z piątego wywołania.

8. Sprawdź działanie aplikacji.

Procedura PobierzLiczbę służy do pobierania liczby całkowitej z określonego przedziału.

Może się okazać, że aplikacja równie często (np. 150 razy) pobiera liczbę rzeczywistą z ograniczone-

go zakresu i programista chciałby użyć funkcji o takiej samej nazwie – PobierzLiczbę, ale do po-

bierania liczb rzeczywistych. Problem ten rozwiązuje mechanizm przeciążania.

Przeciążanie procedur

Każdą procedurę charakteryzuje jednoznacznie pierwszy wiersz jej deklaracji:

Sub Nazwa (Parametr1, Parametr2)

Nazwa procedury oraz liczba, typ i kolejność parametrów (bez opcjonalnych) stanowią zestaw

informacji nazywany sygnaturą procedury. Dwie procedury są różne (to znaczy jednoznacznie rozpo-

znawalne przez kompilator) jeśli posiadają różne sygnatury. Oznacza to, że procedury są różne, jeśli

ich sygnatury różnią się przynajmniej jednym elementem, np. typem trzeciego parametru, liczbą pa-

rametrów itd. Można zatem zbudować dwie procedury o identycznej nazwie, które będą jednak róż -

ne.

Opisany mechanizm nazywa się przeciążaniem, a zdefiniowane w ten sposób procedury –

przeciążonymi. Przeciążanie zilustruje kolejne ćwiczenie.

background image

Ćwiczenie 72
Utworzyć przeciążoną procedurę PobierzLiczbę, która będzie pobierać liczbę rzeczywistą
z określonego przedziału.

1. Skopiuj deklarację procedury PobierzLiczbę i wklej kopię poniżej oryginału. Zauważ, że

nazwa pierwszej „wersji” została podkreślona, a po naprowadzeniu na nią kursora myszy

edytor podpowiada, że:

Public Sub PobierzLiczbę(ByRef PobieranaLiczba As Integer, [Min As Integer = 0], [Maks As In-

teger = 215])' has multiple definitions with identical signatures. – Publiczna procedura PobierzLiczbę

posiada wiele deklaracji z identycznymi sygnaturami.

2. Zmień typ wszystkich parametrów w drugim egzemplarzu procedury z Integer na Double.

Zauważ, że po zmianie typu pierwszego parametru znika błąd sygnalizowany w punkcie

1. ćwiczenia.

3. Zmień tekst zachęty tak, aby użytkownik wiedział, że może wprowadzić liczbę rzeczywi -

stą.

4. Zmień funkcję konwersji CInt na Cdbl.

5. Skompiluj projekt.

Kompilator odróżnia od siebie dwie wersje procedury PobierzLiczbę. Programista może wy-

wołać każdą z nich używając tej samej nazwy. Jeśli użyje jako pierwszego parametru wielkości typu

Integer

, to zostanie użyta jedna wersja, jeśli

Double

– druga. Edytor wspiera programistę wyświetla-

jąc informację o wywoływanej procedurze w specyficzny sposób. Ilustruje to kolejne ćwiczenie.

Ćwiczenie 73
Wykorzystać procedurę PobierzLiczbę do pobrania wartości rzeczywistej.

1. Dodaj na końcu procedury Main pusty wiersz i ustaw w nim kursor tekstowy.

2. Zadeklaruj zmienną dblLiczba typu Double o wartości początkowej 7.

3. Zaprogramuj użycie przeciążonej wersji procedury PobierzLiczbę do pobrania wartości

dla zmiennej dblLiczba, w tym celu:

wpisz nazwę procedury – PobierzLiczbę – i dopisz lewy nawias okrągły,

zauważ, że edytor nieco inaczej przedstawia informację o procedurze i jej parame-

trach:

Rysunek 25: Dodatkowa informacja o istnieniu przeciążonych wersji metody PobierzLiczbę.

background image

użyj klawisza ze strzałką () do wyświetlenia informacji o drugiej wersji procedury:

wpisz nazwę zmiennej dblLiczba i zamknij nawias okrągły.

4. Zaprogramuj wyświetlenie wartości zmiennej dblLiczba.

5. Sprawdź działanie aplikacji.

Kończenie pracy procedury

Działanie procedury kończy się wraz z wykonaniem jej ostatniej instrukcji. Może się jednak oka-

zać, że po wykonaniu pewnej instrukcji procedury dalsza jej praca jest niepotrzebna, albo wręcz

szkodliwa. W takim przypadku do zakończenia procedury można użyć jednej z dwóch instrukcji:

Exit Sub 'Wyjdź z procedury
Return 'Powrót

Liczba wystąpień każdej z tych instrukcji nie jest ograniczona i można je stosować w tej samej

procedurze.

Przykład. Procedura pobiera współczynniki a, b, c równania kwadratowego:

ax

2

bxc=0

Jeśli użytkownik wprowadzi dla a wartość 0, to pobieranie dalszych danych nie ma sensu.

Sub PobierzABC(ByRef a As Double, ByRef b As Double, ByRef c As Double)

Console.Write("Współczynnik a równania wynosi: ")
a = CDbl(Console.ReadLine())
If a = 0 Then Exit Sub
'Dalsze instrukcje

End Sub

Wydaje się, że użycie instrukcji

Exit Sub

, ze względu na słowo kluczowe

Sub

jest bardziej

wskazane – poprawia czytelność kodu. Ponadto słowo

Return

ma szczególne znaczenie dla drugie-

go typu metod, to jest funkcji.

VI.2.

Funkcje – metody typu Function

W wielu programach, albo w wielu miejscach jednego programu, może pojawić się ciąg instruk-

cji, których zadaniem jest obliczenie pewnej wartości. Podobnie, jak dla podprogramów, istnieje moż-

liwość nadania takiemu ciągowi nazwy. Ponieważ efektem wykonania nazwanego zestawu instrukcji

jest raczej określony wynik (wartość funkcji), a nie określony ciąg działań (jak w przypadku procedur),

to procedura taka może być traktowana jako wartość, którą zwraca – może wystąpić po prawej stro-

Rysunek 26: Informacja o sygnaturze drugiej wersji metody PobierzLiczbę.

background image

nie operatora przypisania. Każda funkcja posiada listę parametrów podlegających takim samym za -

sadom jak w procedurach typu

Sub

, ponadto rozważania dotyczące sygnatur i przeciążania przeno-

szą się z procedur na funkcje bez zmian. Podstawowa postać deklaracji funkcji jest następująca:

Function NazwaFunkcji(Parametry) As TypWynikuFunkcji

'Instrukcje funkcji, w tym przynajmniej jedna:
'Return Wyrażenie
'lub
'NazwaFunkcji = Wyrażenie

End Function

Słowa kluczowe

Function

End Function

ograniczają blok instrukcji funkcji. Instrukcja

Return

Wyrażenie służy do przekazania wyniku funkcji procedurze wywołującej. Do tego celu moż-

na użyć również przypisania:

NazwaFunkcji = Wyrażenie

Różnica pomiędzy tymi sposobami jest taka, że pierwszy z nich (Return) kończy działanie bloku

funkcji. W drugim przypadku do zakończenia funkcji można użyć instrukcji

Exit

Function

. Kolejne

ćwiczenie polegać będzie na zdefiniowaniu funkcji, która oblicza wartość wyrazu ciągu:

a

n

=

2n−1

n1

na podstawie wartości n przekazanej jako parametr. Należy zauważyć, że ze względu na uła-

mek występujący we wzorze, wartość wyrazu ciągu, w ogólności, będzie liczbą rzeczywistą.

Ćwiczenie 74
Zdefiniować funkcję WyrazCiągu, która na podstawie parametru n (numeru wyrazu)
oblicza jego wartość.

1. Dodaj pusty wiersz w module Moje_060 i umieść w nim kursor tekstowy.

2. Zdefiniuj funkcję spełniającą postulaty postawione w zadaniu, w tym celu:

wpisz słowo kluczowe Function, a po spacji nazwę funkcji – WyrazCiągu,

otwórz nawias okrągły i zdefiniuj parametr n typu Integer przekazywany przez

wartość i zamknij nawias okrągły,

określ typ wyniku funkcji na Double i naciśnij Enter,

zauważ, że edytor zamyka deklarację funkcji słowami kluczowymi End Func-

tion i umieszcza kursor tekstowy wewnątrz bloku instrukcji.

Przykład.

Function WyrazCiągu(ByVal n As Integer) As Double

End Function

background image

3. Zaprogramuj obliczenie wartości wyrazu ciągu i przekazanie jej jako wyniku funkcji,

w tym celu:

wpisz słowo Return i dopisz spację,

wpisz wyrażenie obliczające wartość wyrazu ciągu na podstawie wzoru podanego

przed ćwiczeniem.

Przykład.

Return (2 * n – 1) / (n + 1)

4. Skompiluj projekt.

Deklaracja funkcji może mieć następującą postać:

Procedurę typu

Function

można wywołać tak, jak procedurę typu

Sub

, jednak w takim przy-

padku następuje utrata wyniku funkcji. Aby nie dopuścić do takiej sytuacji wywołuje się funkcje

po prawej stronie znaku przypisania, lub w jakimkolwiek innym miejscu kodu, w którym można umie-

ścić wartość określonego typu.

Przykłady. Zakłada się, że zmienna dblWyraz jest typu

Double

.

dblWyraz = WyrazCiągu(7)
Console.WriteLine("Wyraz ciągu o numerze {0} ma wartość {1}", 11, WyrazCiągu(11))

Kolejne ćwiczenie wykorzystuje funkcję WyrazCiągu do wypełnienia wartościami tablicy.

Ćwiczenie 75
Utwórz nowy Projekt_075 aplikacji konsolowej i dodaj do niego moduł Moje_060. Użyj
funkcji WyrazCiągu do wypełnienia, wartościami pierwszych pięciu wyrazów ciągu,
tablicy o elementach typu Double. Zaprogramuj wyświetlenie numerów wyrazów i ich
wartości w postaci tabelki w konsoli.

Efekt działania aplikacji może być podobny do przedstawionego poniżej. Wartości wyrazów cią-

gu formatowano funkcją Format, a wyrównanie uzyskano przy użyciu funkcji RSet.

Rysunek 28: Projekt_075 w działaniu.

Rysunek 27: Kod źródłowy funkcji WyrazCiągu.

background image

Funkcje można wywoływać w innych funkcjach, a także w procedurach. Również procedury

można wywoływać w funkcjach lub innych procedurach. Podział funkcji (procedur) na mniejsze funk-

cje (procedury), w oparciu o logiczne przesłanki, połączony z używaniem znaczących nazw dla funk-

cji i procedur powoduje, że kod źródłowy jest czytelny i zwarty – łatwiej odnaleźć w nim potencjalne

błędy.

Funkcję (procedurę) można wywołać także w niej samej – wywołując zjawisko zwane rekuren-

cją.

VI.3.

Rekurencja

Sztandarowym przykładem rekurencji jest matematyczna definicja funkcji silnia:

n !=

{

1 gdy n=0

n⋅n−1 ! gdy n0

Łatwo zauważyć, że funkcja silnia zdefiniowana jest przez samą siebie, to znaczy, aby obliczyć

n! trzeba najpierw obliczyć (n-1)!. Żeby móc obliczyć (n-1)! trzeba wcześniej obliczyć (n-2)! itd. Mo-

głoby się wydawać, że taka deklaracja jest nieskuteczna – „tak można w nieskończoność”. Jednak

oblicza się wartość funkcji silnia, gdyż występuje pewien przypadek elementarny, który kończy obli-

czenia. Koniec obliczeń następuje wtedy, gdy kolejnym argumentem funkcji silnia jest zero. Wtedy

funkcja zwraca wartość 1. Wartość ta mnożona jest przez poprzedni argument, a iloczyn przez po-

przedni itd., aż do osiągnięcia pierwszego wywołania, które zwraca obliczoną wartość.

Prześledźmy to na przykładzie 3!

A) 3

0, dlatego musimy obliczyć (3-1)!, czyli 2!, a wynik pomnożyć przez 3,

B) 2

0, dlatego musimy obliczyć (2-1)!, czyli 1!, a wynik pomnożyć przez 2,

C) 1

0, dlatego musimy obliczyć (1-1)!, czyli 0!, a wynik pomnożyć przez 1,

D) 0=0, z definicji 0! wynosi 1, wartość 1 jako wynik przekazujemy do wiersza C),

E) 1 pomnożone przez 1 daje 1, wartość tą, jako wynik przekazujemy do wiersza B),

F) 1 pomnożone przez 2 daje 2, wartość tą, jako wynik przekazujemy do wiersza A),

G) 2 pomnożone przez 3 daje 6, wartość tą, jako wynik funkcji przekazujemy do miejsca wywo-

łania.

3!=6.

W kolejnym ćwiczeniu nastąpi próba zdefiniowania rekurencyjnego funkcji silnia. Przyjrzyjmy się

jeszcze raz definicji:

n !=

{

1 gdy n=0

n⋅n−1 ! gdy n0

background image

Można ją odczytać następująco: Jeśli argument (parametr) funkcji jest równy zero, to funkcja

zwraca wartość 1, jeśli jest większy niż zero funkcja zwraca wartość iloczynu argumentu i wartości

funkcji silnia dla argumentu pomniejszonego o jedność. Ponieważ występują dwa przypadki n=0 i

n>0, to wydaje się, że blok funkcji może zawierać konstrukcję warunkową

If

Then

Else

End If

. Warunek powinien być wyrażeniem sprawdzającym czy argument jest równy zeru. Naszki-

cujmy szkielet funkcji:

Function Silnia(ByVal n As Integer) As Integer

If n = 0 Then

Else

End If

End Function

Zdanie: Jeśli argument (parametr) funkcji jest równy zero, to funkcja zwraca wartość 1, tłuma-

czy się na język programowania bardzo prosto:

Return 1

Również drugie zdanie: Jeśli argument (parametr) funkcji jest większy niż zero, to funkcja zwra-

ca wartość iloczynu argumentu i wartości funkcji dla argumentu pomniejszonego o jedność, można

łatwo przetłumaczyć:

Return n * Silnia(n – 1)

W ostateczności funkcja przyjmie postać:

Function Silnia(ByVal n As Integer) As Integer

If n = 0 Then

Return 1

Else

Return n * Silnia(n – 1)

End If

End Function

Ćwiczenie 76
Utworzyć nowy projekt aplikacji konsolowej i zdefiniować w jego części deklaracyjnej
funkcję Silnia. Zaprogramować obliczenie i wyświetlenie pierwszych sześciu wartości
funkcji.

Rysunek 29: Projekt_076 w działaniu.

background image

Funkcja Silnia, której typ zdefiniowano na

Integer

nie pozwala obliczać wartości n! dla n więk-

szych niż 12. Można to poprawić zmieniając typ wyniku na Int64 (n <= 20). Jeśli nie zależy nam na

dokładnej wartości całkowitej, to można zwiększyć możliwości funkcji stosując typy rzeczywiste:

Sin-

gle

– n <= 34,

Double

– n <= 170. Przy tym, w przypadku typów rzeczywistych, przekroczenie poda-

nego zakresu powoduje, że wynik zwracany przez funkcję to „+nieskończoność”.

W matematyce występuje wiele deklaracji, które można zaimplementować rekurencyjnie. Kolej-

nym przykładem jest funkcja obliczająca wartość tzw. Symbolu Newtona. Z czworga dzieci można

wybrać sześć różnych par:

1 2

1 3

1 4

2 3

2 4

3 4

Liczbę możliwych do ustawienia, różnych układów składających się z k elementów, przy wybo-

rze ze zbioru n elementów określa właśnie funkcja Symbol Newtona (

n
k

– czyta się „n nad k”)

określona wzorem:

n
k

=

n !

k !⋅ nk !

Pamiętając, że 0! jest równe 1, można zauważyć, że jeśli k jest równe zeru, to wzór sprowadza

się do

n !
n !

, a to niezależnie od wartości n wyniesie 1. Podobnie, jeśli k jest równe n, to wzór ponow-

nie sprowadza się do

n !
n !

, czyli do jedności. Korzystając z definicji silni można przepisać wzór ogól-

ny w następujący sposób:

n
k

=

n !

k !⋅nk !

=

n⋅n−1!

k⋅ k −1!⋅nk !

=

n
k

n−1!

k −1!⋅nk !

Napiszmy poniżej wzór dla wartości n-1 i k-1:

n−1
k −1

=

n−1!

k −1!⋅ n−1−k −1!

=

n−1!

k −1 !⋅ n−1−k 1!

=

n−1!

k −1!⋅nk !

Porównując wzory (patrz: strzałka) można zauważyć, że:

n
k

=

n
k

n−1
k −1

background image

Zbierając wszystkie wzory w jedną deklarację otrzymujemy „ładną”, rekurencyjną deklarację

funkcji.

n
k

=

{

1 gdy k =0

1 gdy k =n

n

k

n−1
k −1

gdy k ≠0 i kn

W powyższym wzorze „zgubiła się” funkcja silnia. To pierwszy sukces rekurencji w tym przypad-

ku, gdyż korzystanie z dokładnych wartości funkcji silnia ograniczało jej argument do liczby 20. Drugi

sukces polega na tym, że obliczanie wartości funkcji Symbol Newtona na podstawie pierwotnej defi -

nicji wymagało wykonania n+k+(n-k)+2=2n+2 mnożeń (w tym jedno dzielenie). Definicja rekurencyjna

pozwala ograniczyć liczbę mnożeń (i dzieleń) do 2k. Przy k bliskich n zysk nie jest wielki, jednak

można zastosować pewną tożsamość, a mianowicie:

n
k

n

nk

która powoduje, że n-k jest bliższe zeru. Tożsamość ta pozwala zredukować dolny argument do war-

tości mniejszych niż n/2, zatem zmniejsza liczbę mnożeń i dzieleń do wartości mniejszej niż n. Wyni -

ka stąd, że korzystanie z rekurencji skróci czas obliczeń o więcej niż połowę, ponadto umożliwi obli -

czenia dla argumentów większych niż 20. Biorąc powyższe pod uwagę napiszemy ostateczny wzór:

n
k

=

{

1 gdy k=0

1 gdy k =n

n

k

n−1
k −1

gdy k ≠0 i k n i 2k ≤n

n

nk

gdy k ≠0 i k n i 2kn

Pierwsze dwa warunki można sprawdzać łącznie, gdyż funkcja zwraca tą samą wartość. Pe-

wien problem stanowi ułamek w trzecim wierszu definicji. Polega on na tym, że Symbol Newtona

z zasady jest liczbą całkowitą, a dzielenie w ogólności daje liczbę rzeczywistą. Będzie ona oczywi-

ście pozbawiona części ułamkowej, jednak kompilator będzie proponował zastosowanie funkcji kon-

wersji. Jednak pamiętając o tym, że wynik funkcji jest całkowity, można problem obejść stosując ope -

rator dzielenia całkowitego „\”, który daje wynik całkowity. Przy tym, ponieważ obliczenie wartości

funkcji musi nastąpić, to należy wywołanie funkcji ustawić jako pierwsze w wyrażeniu:

Return SymbolNewtona(n - 1, k - 1) * n \ k

W kolejnym ćwiczeniu należy zdefiniować funkcję w oparciu wzór:

n
k

=

{

1 gdy k=0

1 gdy k =n

n

k

n−1
k −1

gdy k ≠0 i k n i 2k ≤n

n

nk

gdy k ≠0 i k n i 2kn

background image

Ćwiczenie 77
Utworzyć nowy projekt aplikacji konsolowej i zdefiniować w części deklaracyjnej modułu
rekurencyjną funkcję SymbolNewtona typu Integer o dwóch parametrach n, k typu
Integer przekazywanych przez wartość realizującą wzór podany przed treścią ćwiczenia.
Zaprogramować wyświetlenie wartości funkcji dla n zmieniających się od 0 do 14 i k
zmieniających się od 0 do n.

Efekt działania aplikacji (zastosowano funkcję RSet) może być taki, jak na rysunku (jest to, tzw.

trójkąt Pascal’a):

Nie zawsze rekurencja pozwala osiągnąć zmniejszenie czasu pracy funkcji, a w niektórych

przypadkach powoduje nawet efekt odwrotny. Przykładem jest ciąg Fibbonacciego zdefiniowany

w następujący sposób:

F

n

=

{

1 gdy n=0
1 gdy n=1

F

n−2

F

n−1

gdy n1

Sprawdzenie działania rekurencyjnej deklaracji tego ciągu pozostawia się ćwiczącym do samo-

dzielnego wykonania. Ciekawym doświadczeniem będzie próba wyświetlenia pierwszych 50 wyrazów

ciągu (ustalić typ funkcji na Int64). Z pewnością (od wartości n większych niż 30) da się zaobserwo -

wać znaczny wzrost czasu obliczania kolejnych wartości.

Również procedury można programować rekurencyjnie. Rozważmy następujący przykład. Pro-

gram losuje liczbę z przedziału <1, 100> i dalsze jego działanie polega na kilkukrotnym sprawdzeniu

czy liczba podana przez użytkownika jest równa wylosowanej. Liczba prób może być ustalana jako

poziom trudności zadania i powinna być parametrem procedury. Również liczba wylosowana przez

program powinna być przekazana do procedury jako parametr.

Jakie działania powinna wykonywać potencjalna procedura realizująca sprawdzenie. Z pewno-

ścią powinna pobrać liczbę typowaną przez użytkownika. Mogą wystąpić trzy przypadki:

Liczba podana przez użytkownika jest równa liczbie wylosowanej przez program. Procedura

powinna powiadomić o tym użytkownika i zakończyć pracę (Exit Sub).

Rysunek 30: Projekt_077 w działaniu.

background image

Liczba podana przez użytkownika jest za mała. Procedura powinna powiadomić o tym użyt-

kownika.

Liczba podana przez użytkownika jest za duża. Procedura powinna powiadomić o tym użyt-

kownika.

Dwa ostatnie przypadki nie kończą procedury, ale kończą próbę podjętą przez użytkownika, na-

leży to uwzględnić zmniejszając o jeden liczbę pozostałych prób. Dalszym działaniem powinno być

sprawdzenie, czy użytkownikowi pozostały jeszcze próby. Mogą wystąpić dwa przypadki:

Liczba pozostałych prób wynosi zero. Procedura powinna powiadomić o tym użytkownika i za-

kończyć pracę.

Pozostały jeszcze próby. Należy wykonać kolejne sprawdzenie. Procedura powinna wywołać

samą siebie, przekazując wylosowaną liczbę oraz liczbę pozostałych prób.

Kolejne ćwiczenie polegać będzie na stworzeniu rekurencyjnej procedury sprawdzającej i wyko -

rzystanie jej w programie.

Ćwiczenie 78
Utworzyć nowy projekt aplikacji konsolowej. Zdefiniować w części deklaracyjnej modułu
procedurę Sprawdź, o dwóch parametrach typu Integer przekazywanych przez wartość
(wylosowana liczba, liczba prób), realizującą opisany wcześniej schemat działania. W
programie zainicjować generator liczb losowych, zaprogramować losowanie liczby z
zakresu <1, 100> i uruchomić procedurę z parametrami: wartość wylosowanej liczby i 7.

Efekt działania aplikacji może być taki, jak na rysunku:

Rysunek 31: Projekt_078 w działaniu - sukces użytkownika.

background image

lub taki jak na kolejnym rysunku:

Rysunek 32: Projekt_078 w działaniu - porażka użytkownika.


Document Outline


Wyszukiwarka

Podobne podstrony:
LAB PROCEDURY I FUNKCJE
Procedury i funkcje
Procedury i funkcje trybu grafi Nieznany
Specjalne ćwiczenia przygotowawcze stosowane przy nauce pływania kraul na grzbiecie
procedury i funkcje
04 Petle programowe cwiczenia przygotowujace
2014 06 konspekt-final, Różne, Przygotowanie do ŚDM w Krakowie 2016 rok, Grudzień 2013 rok, Styczeń
PAS procedury funkcje (2)
wykład 4 procedury, funkcje, sekwencje, paczki, wyzwalacze
Wykład 6D 06 05 2014 Ćwiczenie 11 KNR y NORMY NAKŁADÓW RZECZOWYCH (2)
pk procedury i funkcje
02 Wprowadzenie do Visual Basic cwiczenia przygotowujace
08 Debugowanie cwiczenia przygotowujace
funkcjonowanie cwiczenia3, Funkcjonowanie Unii Europejskiej FUE
L.A. Konspekt - Ćwiczenia przygotowujące do nauczania trójskoku i startu niskiego, AWF, Lekkoatletyk

więcej podobnych podstron