BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











Tablice w Visual Basic .NET

22-12-2003 14:10 | User 79745
Nowości, zastosowania i wskazówki

Wprowadzenie

Tablice są konstrukcjami strukturalnymi wykorzystywanymi praktycznie w każdym języku programowania. Umożliwiają przechowywanie wielu danych tego samego typu w jednej zwartej strukturze. Tablice umożliwiają odwoływanie się do serii zmiennych za pomocą nazwy tablicy oraz zmiennej indeksującej, lub krócej indeksu, czyli liczby określającej położenie elementu w tablicy. Liczba indeksów w tablicy zależy od tzw. wymiarów tablicy. Podstawowym typem tablicy jest tablica jednowymiarowa, indeksowana za pomocą jednego indeksu. W przypadku większej ilości wymiarów, liczba indeksów odpowiednio rośnie. Tablice wielowymiarowe są przydatne podczas wykonywania złożonych obliczeń numerycznych, czy operacji na macierzach. W niniejszym artykule omówimy tablice w oparciu o bibliotekę operacji na macierzach, która stanowi doskonały przykład zastosowań tablic dwuwymiarowych, oraz nowości, jakie pojawiły się w nowym wydaniu środowiska Visual Basic.

Tablice jako obiekty

Budując platformę .NET i wprowadzając do niej język Visual Basic Microsoft całkowicie zmienił koncepcję postrzegania tablic w tym języku programowania. Dotychczas tablice w środowisku Visual Basic były prostym typem danych. W nowej odmianie środowiska główny nacisk położono na programowanie obiektowe, które stało się integralną częścią nowej technologii, więc siłą rzeczy tablice również awansowały do rangi obiektów.

Podstawą tworzenia wszystkich tablic w Visual Basic .NET jest klasa System.Array zdefiniowana w .NET Framework. Zawiera ona szereg metod umożliwiających tworzenie tablic i manipulowanie nimi. Wprowadza ponadto kilka nowych konstrukcji, które są zalecane w zastosowaniach przez firmę Microsoft, a do których programiści poprzednich edycji powinni się przyzwyczaić. Kompatybilność z poprzednimi wersjami języka została zachowana przez wprowadzenie w .NET Framework klasy Microsoft.VisualBasic implementującej stare wersje podstawowych poleceń. Microsoft zaleca korzystanie z metod tej klasy w bardzo ograniczonym stopniu. Metody tej klasy implementują przestarzałe już technologie, a dodatkowo powodują spowolnienie działania programu, gdyż za każdym razem, gdy wywoływana jest funkcja musi nastąpić odwołanie do odpowiedniej metody w bibliotece odpowiadającej za kompatybilność.

Elementem wyróżniającym tablice od klasycznych obiektów jest brak konieczności wykorzystywania słowa kluczowego NEW podczas tworzenia nowej instancji, co pozwala na deklarowanie tablic w „starym” stylu:

Deklaracja Dim aVector(5) as Single

spowoduje utworzenie w pamięci 5 - elementowej tablicy elementów typu Single. Odwołania do elementów tablicy również pozostały niezmienione w stosunku do poprzedniej wersji języka – element wskazuje się przez podanie nazwy tablicy i umieszczenie w nawiasach okrągłych jego indeksu.

Nowością w Visual Basic .NET jest indeksowanie tablic od 0. Poprzednie edycje umożliwiały podanie zakresu indeksów podczas deklaracji tablic. Nowa wersja domyślnie nie posiada takiej opcji, choć jest możliwe wymuszenie utworzenia tablicy o indeksach np. od 8 do 20:

Dim aTablica as Array = System.Array.CreateInstance(GetType (Integer), _
                        New Integer(){8}, New Integer(){20})

Operacja ta jest jednak nieco karkołomna.

Elementem nowym, przy czym bardzo przydatnym jest możliwość inicjowania tablicy w trakcie jej deklaracji. Wykonuje to konstrukcja:

Dim aTablica() as Integer = {1,2,2,4,5,6,7}

W nawiasach klamrowych podaje się wartości elementów składowych tablicy. Stanowi to dużą pomoc przy deklarowaniu niewielkich tablic.

Jedną z poważniejszych zmian w środowisku .NET jest przydzielenie tablic do typu referencyjnego. Powoduje to, że nazwa tablicy staje się wskaźnikiem do miejsca w pamięci, w którym się znajduje. Takie podejście ma swoje zalety, ale również i wady. Zaletą jest uproszczenie obsługi tablic dynamicznych, o których będzie mowa dalej oraz zwiększona szybkość działania. Wadą natomiast jest konieczność kopiowania danych do tablicy pomocniczej, w przypadku operacji wymagających modyfikacji elementów tablicy, bez ich utraty.

Praca z tablicami

Podczas budowania biblioteki operacji macierzowych podstawowym wykorzystywanym typem tablic są tablice dynamiczne. Tablicę taką deklaruje się przez podanie nazwy tablicy oraz pustych nawiasów okrągłych. Za nawiasami określany jest typ danych elementów w tablicy:

Dim aVector() as Single

Aby zadeklarować dynamiczną tablicę wielowymiarową, w nawiasie należy postawić tyle przecinków ile ma wynosić rozmiar tablicy pomniejszony o 1:

Dim aMatrix(,) as Single

Powyższa deklaracja tworzy dynamiczną tablicę dwuwymiarową.

Ponieważ tablice są typem referencyjnym, taka deklaracja nie wystarcza, aby rozpocząć pracę z tablicą. Próba odwołania do takiej tablicy zakończy się wygenerowaniem wyjątku NullReferenceException. Aby korzystać z tablicy dynamicznej, trzeba określić jej rozmiar. Realizuje to komenda ReDim, która przydziela pamięć dla tablicy. W przypadku zmiany rozmiaru tablicy już istniejącej, ReDim wyczyści jej zawartość. Aby tego uniknąć można posłużyć się przyrostkiem Preserve, który powoduje zachowanie pierwszych n elementów w tablicy podczas zmiany jej rozmiaru.

Podczas pracy z tablicami wielu początkujących programistów wykorzystuje w pętlach maksymalne indeksy wpisywane „na sztywno”. Takie podejście najczęściej prowadzi do zawieszenia programu w wyniku wygenerowania wyjątku IndexOutOfRangeException. Co prawda, można przechwytywać ten wyjątek i go obsługiwać, ale skuteczniejszym rozwiązaniem jest dynamiczne określanie zakresu indeksów tablicy.

W poprzedniej edycji Visual Basic realizowały to dwie funkcje:

•     Ubound(nazwa_tablicy, numer_wymiaru) – określająca górny graniczny indeks tablicy

•     LBound(nazwa_tablicy, numer_wymiaru) – określająca dolny graniczny indeks tablicy

Nowa edycja środowiska posiada odpowiadające im metody wbudowane w klasę System.Array.

•     GetUpperBound(numer_wymiaru)     

•     GetLoweBound(numer_wymiaru)

 

Umożliwiają one dostęp do indeksów granicznych tablicy nie przez przekazanie tablicy jako argumentu jak to miało miejsce poprzednio, ale bezpośrednio – jako metoda klasy. Taka integracja powoduje wzrost szybkości działania kodu.

Mając określone indeksy graniczne można bezpiecznie poruszać się w obrębie tablicy o dowolnym rozmiarze, bez obawy o wyjątki spowodowane przekroczeniem granicznego indeksu tablicy.

Jako przykład podsumowujący dotychczasowe wiadomości przytoczę operację transponowania macierzy:

Shared Sub TransposeMatrix(ByVal aMatrixIn(,) As Single, _
                          ByRef aMatrixOut(,) As Single)
       Dim i, j As Byte
       Dim xSize As Byte = aMatrixIn.GetUpperBound(0)
       Dim ySize As Byte = aMatrixIn.GetUpperBound(1)
       ReDim aMatrixOut(ySize, xSize)
       For i = 0 To xSize
           For j = 0 To ySize
               aMatrixOut(j, i) = aMatrixIn(i, j)
           Next
       Next
End Sub

Procedura ta pobiera 2 argumenty:

•     aMatrixIn - macierz wejściową (która ma być transponowana),

•     aMatrixOut - wskaźnik do macierzy wyjściowej (zadeklarowanej jako tablica dynamiczna bez określania indeksów).

Z racji tego, że tablice są typem referencyjnym, zmiana rozmiaru macierzy wyjściowej nie jest ograniczona zasięgiem tylko do procedury lokalnej. Modyfikuje ona obszar pamięci wskazywany przez aMatrixOut, a więc jest widoczna również poza tą procedurą – w procedurze wywołującej. Kolejną rzeczą, na którą chciałbym zwrócić uwagę szczególnie początkującym programistom jest tzw. buforowanie zmiennych wykorzystywanych w pętlach. Wielu programistów wykorzystuje metody wyznaczania indeksów granicznych bezpośrednio w pętli:

     

For i = 0 To aMatrixIn.GetUpperBound(0)
For j = 0 To aMatrixIn.GetUpperBound(1)
          aMatrixOut(j, i) = aMatrixIn(i, j)
     Next
Next

Taka konstrukcja pod względem wydajności kodu nie jest optymalna, ponieważ podczas każdego przebiegu pętli wywoływana jest funkcja określająca górny rozmiar tablicy. Wywołanie funkcji obliczającej tę wartość jest znacznie wolniejsze niż odwołanie się do zmiennej, która zawiera już obliczony indeks graniczny. Dlatego też należy najpierw przypisać odpowiednim zmiennym indeksy graniczne, a dopiero potem pobierać je w pętli ze zmiennych, a nie obliczając za każdym razem.

Omawiając pracę z tablicami w .NET warto wspomnieć o alternatywnej metodzie przypisywania wartości poszczególnym komórkom Tablicy. Jak powszechnie wiadomo służy do tego operator „=”.  Zamiennikiem tego sposobu przypisywania wartości jest wykorzystanie metody klasy Array o nazwie SetValue. Metoda ta może zostać przeciążana w klasach wyprowadzonych z klasy Array, a jej podstawowe wywołanie ma postać:

tablica.SetValue(obiekt,index)

Dziwić może, dlaczego funkcja pobiera jako argument obiekt, a nie wartość. Otóż w Visual Basic .NET wszystkie podstawowe typy danych są obiektami, stąd też taki zapis obowiązuje w dokumentacji środowiska (na potrzeby artykułu został nieznacznie uproszczony), a oznacza po prostu pobranie wartości, która ma zostać zapisana w polu o podanym indeksie.

Istnieje cały szereg dodatkowych metod, które są pomocne podczas pracy z tablicami. Jedną z nich jest funkcja BinarySearch służąca do binarnego przeszukiwania tablicy. Jako argumenty pobiera nazwę tablicy i wartość szukanego elementu. Funkcja jest bardzo przydatna, ale aby zadziałała poprawnie, należy przekazać jej posortowaną tablicę. Niektórych programistów ucieszy fakt, że sortowanie tablic zostało „natywnie” wbudowane w klasę Array i służy do tego funkcja Sort. Zwalnia ona programistę z pisania dodatkowego kodu. Przydatną w niektórych przypadkach jest metoda Reverse odwracająca kolejność elementów w tablicy. Klasa Array posiada również przydatną metodę Clear kasującą jej zawartość i przydzieloną pamięć. Wynikiem operacji jest przydzielenie identyfikatorowi tablicy wskaźnika pustego Nothing.

Przykłady kodu źródłowego pochodzą z załączonej biblioteki operacji macierzowych.

 

Jarosław Cisowski

Załączniki:

tagi: Visual Basic

Podobne artykuły

Komentarze 0

pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.