Programujeme ve Visual Basic .NET - 20. díl - deklarace pole

V následujících třech dílech seriálu o programovacím jazyku VB.NET se budeme věnovat datovému typu pole. V tomto dílu si tuto datovou strukturu přiblížíme a seznámíme se s nejobvyklejšími způsoby její deklarace.

Datový typ pole

Pole (v angličtině "array") je datová struktura, kterou si můžeme představit jako blok proměnných téhož názvu (tzv. prvků, položek "items"), ke kterým můžeme přistupovat podle pořadového čísla (tzv. indexu). Datový typ pole zavádíme do programu vždy, když potřebujeme pracovat s bloky proměnných stejného typu (řádkem, tabulkou, nebo maticí čísel). Jak si ukážeme později, prostředí VB.NET umožňuje s textovými řetězci zacházet jako s polem znaků. Z hlediska členění datových typů i použití je důležité, že pole je referenční typ, tj. v parametrech procedur se předávají vždy odkazem a při přiřazení jedné proměnné typu pole do druhé nedochází k vytvoření kopie. Je to pochopitelné, protože pole mohou být libovolně rozsáhlá a jejich velikost je omezena prakticky jen množstvím volné paměti, která je spravována .NET runtime CLR (tzv. "halda", neboli managed heap).

Klepněte pro větší obrázek
Ukázka jednorozměrného pole třinácti prvků a(i) s indexem i

Ačkoliv je fyzické uspořádání prvků pole v paměti vždy lineární a jednotlivé prvky pole obsazují sousedící místa v paměti, většina vyšších programovacích jazyků včetně VB.NET umožňují přistupovat k prvkům pole, jako kdyby to byly N-rozměrné tabulky (tzv. matice). VB.NET (stejně jako ostatní jazyky .NET) podporuje až 32-rozměrná pole, čili pole adresované až 32 indexy, s větším počtem rozměrů než tři (používaným ve 3D grafice) se ale setkáme málokdy.

Klepněte pro větší obrázek
Příklad dvourozměrného pole a(i,j) s indexy i, j o rozměrech 10 x 5

Deklarace polí

Ve VB.NET lze pole stejně jako ostatní proměnné deklarovat příkazem Dim, za kterým následuje název pole, doplněný závorkami, popřípadě specifikací datového typu pomocí klíčového slova As. V závorkách přitom může být uveden - ale nemusí - požadovaná velikost pole. Číslo označuje horní index pole, jehož prvky jsou číslované od nuly, pročež skutečný počet prvků v poli bude vždy o jedničku vyšší, než horní index pole. V případě, že používáme místo deklarace typu pomocí As specifikátor typu, musí být připojen za jménem proměnné. Všechny deklarace pole uvedené níže jsou v důsledku toho ekvivalentní (pro lepší přehlednost je zvykem názvy proměnných typu pole začínat předponou a- ("array"):

    Dim aPole%()
    Dim aPole() As Integer
    Dim aPole() As System.Int32

Výsledkem deklarace pole s pevnou délkou je vyhrazení (tzv. alokace) potřebného místa na haldě - např. ukázka níže deklaruje pole s deseti prvky, jejichž index se mění v rozsahu od nuly do devíti (0 - 9):

    Dim aPole(9)

Uvedený příklad tedy deklaruje pole o názvu aPole s počtem deseti prvků. Název pole lze volit libovolně, ale v našich příkladech budeme proměnné typu pole označovat názvem, začínajícím na písmeno a, abychom je na první pohled odlišili od obvyklých proměnných. V případě vícerozměrného pole se do závorky postupně uvádí rozsah jednotlivých rozměrů (tzv. dimenzí) pole, např. dvourozměrnou matici 5 x 3 prvků deklarujeme následovně:

    Dim aPole(4, 2)

Jelikož celkový počet prvků pole je dán součinem všech dimenzi, počet prvků vícerozměrných polí se může se změnou vyšších dimenzí měnit velmi rychle, proto k deklaraci polí přistupujme s rozmyslem. Horní hranice velikosti pole je v podstatě limitovaná pouze dostupným množstvím volné paměti (výchozí deklarace pole alokuje čtyř bajty na každý prvek pole). V případě, že v okamžiku deklarace pole není volná paměť pro operační systém dostupná, VB.NET nám  oznámí chybu (výjimku) typu System.OutOfMemoryException. Později se naučíme, jak podobných chybám předcházet tím, že si předem zjistíme velikost dostupné paměti.

Čas od času vznikne potřeba inicializovat pole nulové délky (např. v případě metod, předávajících referencí parametr typu pole). Takové pole se ve VB.NET uzančně deklaruje s rozměrem -1 (pozor, nikoliv 0, neboť  příkaz Dim a(0) deklaruje pole s jedním prvkem):

    Dim aZero(-1) As Single

Namísto čísla může deklarace pole obsahovat libovolný výraz, který lze v rámci běhu programu vyhodnotit a převést jako celé kladné číslo, včetně použití názvů konstant a proměnných. Níže uvedená deklarace pole je tudíž z hlediska VB.NET korektní (hodnota nMax je před použitím v deklaraci pole automaticky zaokrouhlena na nejbližší celé číslo), v zájmu přehlednosti a reprodukovatelnosti funkce programu bychom však měli této flexibility VB.NET využívat co nejméně:

    Dim N As Double = 4.3
    Dim aObject(N + 1, (2 + 1) * 3)

Dynamická deklarace a předimenzování polí

V případě, že při deklaraci pole použijeme výraz, který nelze vyhodnotit jako celočíselné kladné číslo, prostředí VB.NET vygeneruje chybu (výjimku typu System.InvalidCastException). Podobným chybám (a také v případě, že velikost rozsahu indexů pole neznáme předem) se můžeme vyvarovat tím, že hodnoty z deklarace pole vypustíme a ponecháme jen čárky, jejichž počet odpovídá předpokládanému počtu rozměrů pole. Tomuto způsobu deklarace se říká tzv. dynamická deklarace pole, například:

    Dim a(), aObject(,,) 

Na rozdíl od předchozích verzí Visual Basic-u překladač VB.NET nepodporuje ani tzv. statická pole (pole pevné délky, jejichž počet rozměrů nelze dodatečně změnit), ani definování spodní hranice indexu pole (tzn. všechny indexy polí ve VB.NET jsou počítány vždy od nuly) známé ze starších verzí VB a ve VB.NET chybí VB příkaz Option Base, jelikož byl zdrojem častých komplikací při migraci zdrojových kódů z různých aplikací.

Ačkoliv pole VB.NET lze kdykoliv dle potřeby předimenzovat, VB.NET neumožňuje pracovat s poli tak flexibilně, jako např. JavaScript a JScript.NET, kde se při vložení prvku s indexy vyššími, než aktuálně nastavené meze pole automaticky předimenzuje na odpovídající délku. Je tedy zapotřebí rozlišovat pojmy dynamické pole, dynamická deklarace pole a dynamické dimenzování pole. Dokud není horní hranice pole specifikována, s polem nelze pracovat a pokus o přístup k neexistujícímu prvku vyvolá výjimku typu System.IndexOutOfRangeException. Před prvním použitím dynamicky deklarovaného (bezrozměrného) pole je tedy nutné specifikovat aktuální počet prvků pole příkazem ReDim - s ohledem na deklaraci uvedenou výše, například:

    ReDim a(5), aObject(3, 2, 1) 

Je nutné si uvědomit, že výchozí volání  příkazu ReDim celý obsah pole vymaže a nahradí dalším polem nové velikosti, jehož všechny prvky jsou v neinicializovaném stavu (tj. mají hodnotu Nothing), může to být tedy v případě rozměrnějších polí časově náročná operace. V případě že nám toto chování nevyhovuje, můžeme při změně rozměrů pole zachovat  hodnoty, které předimenzování neovlivní klíčovým slovem Preserve, například:

    Dim aObject(3, 2)
    ReDim Preserve aObject(3, 6) 

V předchozí ukázce jsme příkazem ReDim pole aObject předimenzovali (v druhém rozměru zvětšili), přičemž prvky s indexy v rozsahu (3,2) zůstaly zachovány. Je nutno zdůraznit, že s ohledem na fyzické uložení pole v paměti lze příkazem ReDim měnit vždy jen nejnižší rozměr pole (v deklaraci vypisovaný na posledním místě vpravo).

Celkový počet rozměrů vrací metoda Rank(), zatímco aktuální velikost rozměru pole na dané pozici vrací metoda GetLength(), kterou každé objekt typu pole disponuje.  Celkový počet prvků ve všech dimenzích pole dohromady pak vrací metoda Length() vracející datový typ Integer (System.Int32), pro zvlášť rozměrná pole je nutno použít metodu LongLength() vracející typ Long (System.Int64).

    Dim aInt(3, 2) As Integer
    Console.WriteLine(aInt.Length)
    Console.WriteLine(aInt.GetLength(aInt.Rank - 1))

Ukázka pro danou deklaraci vypíše celkový počet prvků v poli aInt a následně počet prvků v nejvyšším rozměru pole (tj. poslední dimenze pole, počítané zprava). Vzhledem k tomu, že pole VB.NET jsou (stejně jako jejich dimenze) číslována (indexována) vždy od nuly, je pochopitelně počet prvků o jedničku vyšší, než maximální index pole:

12
3

Pole polí, nepravidelná pole

Z definice polí vyplývá, že počet prvků ve všech rozměrech téže úrovně musí zůstat shodný - tzv. pravidelná (pravoúhlá) pole tvoří N-rozměrnou tabulku). Jelikož však pole typu System.Object může obsahovat proměnnou libovolného typu, nic nám nebrání přiřadit prvkům pole proměnné typu pole různého počtu typu a prvků. Na jednotlivé vnořené prvky složených polí se pak můžeme odkazovat indexy, postupně uzavíraných do závorek:

   Dim aObj(2) As Object, aDbl0(2) As Double, aDbl1(3) As Double, aDbl2(1) As Double
   aObj(0) = aDbl0: aObj(1) = aDbl1: aObj(2) = aDbl2
   aObj(1)(2) = 5.31

Problémem tohoto přístupu je chybějící typová kontrola na nejvyšší úrovni a relativní pomalost práce VB.NET s proměnnými obecného referenčního typu. Jelikož jsou všechna pole odvozena od třídy System.Array, můžeme být při deklaraci konkrétnější a deklarovat tzv. pole polí, se kterým VB.NET zachází mnohem efektivněji:

   Dim aArr(2) As System.Array, aDbl() As Double = {0.5, 6D}
   aArr = New Array(2){New Double(2){2, 3.4, 1E-4D}, New Double(3){}, New Double(1){0.2, Math.PI}} 
   aArr(0) = aDbl
   aArr(1)(2) = 5.31

V případech, kdy máme zaručeno, že všechny prvky tvoří pole stejného typu, můžeme využít podporu syntaxe VB.NET pro deklarace tzv. nepravidelných polí:

   Dim aDbl()() As Double  = {New Double(2){0.2, 1, 7}, New Double(1){}}
   aDbl(1) = New Double(2){6D, 4, -1E-1}
   aDbl(0)(2) = 11.11
   Redim aDbl(1)(2)
   aDbl(1)(2) = 0

Nepravidelná (nepravoúhlá) pole se v angličtině označují jako tzv. "zubatá" (jagged arrays), jelikož nepravidelné střídání délky polí může připomínat zuby hřebenu:

Klepněte pro větší obrázek

Oproti pravoúhlým mají nepravidelná pole tu výhodu, že na ně lze aplikovat stejné metody, jako na jednotlivé dimenze pravoúhlého pole (např. metodu Sort(), Reverse(), nebo BinarySearch(),  jak si ukážeme později). Deklarace pravoúhlých a nepravidelných polí lze vzájemně kombinovat a vytvářet tím složité vícerozměrné struktury. V případě, že se počet prvků v různých rozměrech výrazně liší, může použití nepravidelného pole ušetřit paměť, kterou by jinak obsadily nevyužité prvky pravoúhlého pole. A v neposlední řadě je práce s nepravidelným polem asi o 20% rychlejší, než práce s pravoúhlým polem a díky vnitřním optimalizacím je srovnatelná s rychlostí jednorozměrného pole se stejným počtem prvků. Tyto a další přednosti vyvažují o něco větší složitost a nepřehlednost kódu při práci s nepravidelným polem.

Stručné shrnutí

Datový typ pole představují efektivní způsob práce s větším počtem proměnných stejného typu. Pole VB.NET jsou dynamická a jejich nejnižší rozměry lze po deklaraci zvětšovat či zmenšovat příkazem ReDim. Po deklaraci lze k prvkům pole přistupovat pomocí indexu podobně jako k obvyklým proměnným. Pro práci s polem nepravidelným počtem prvků v každém rozměru a urychlení přístupu k jednotlivým prvkům může být výhodná deklarace nepravoúhlého pole.

V následujícím dílu se seznámíme se základními způsoby inicializace a použití pole, např. jejich procházení pomocí příkazů cyklu For-Next a For-Each.

Témata článku: Software, Programování, Nejnižší hranice, Stejný prvek, Double, Celková hodnota, Celková velikost, Libovolné místo, Jednotlivé prvky, Klíčový parametr, Kladné číslo, Pre, Různá místa, Jednotlivá místa, Reversi, Nejnižší úroveň, Stejná metoda, Pole, Díl, Dostupné místo, Dimenze, Klíčový prvek, Index

Určitě si přečtěte

Budoucností Windows 10 je Fluent Design. Takto bude jednou vypadat celý systém

Budoucností Windows 10 je Fluent Design. Takto bude jednou vypadat celý systém

** Fluent Design je vzhled, do kterého postupně Microsoft převleče celý systém ** Staví na průhlednosti a velkých plochách ** Do Windows 10 se z části dostane už zítra při vydání podzimní aktualizace

16.  10.  2017 | Stanislav Janů | 146

Velká podzimní aktualizace Windows 10 je tady: Co přináší Fall Creators Update

Velká podzimní aktualizace Windows 10 je tady: Co přináší Fall Creators Update

** Po půl roce je tu další aktualizace Windows ** A opět přináší hlavně hromadu drobných kosmetických vylepšení ** Podívali jsme se na ty nejzajímavější

Včera | Jakub Čížek | 93


Aktuální číslo časopisu Computer

Nový seriál o programování elektroniky

Otestovali jsme 17 bezdrátových sluchátek

Jak na nákup vánočních dárků ze zahraničí

4 tankové tiskárny v přímém souboji