V předchozím dílu jsme se seznámili s hodnotovým typem pole (System.Array) a základními typy a jejich deklarací. Podobně jako jednoduché proměnné pole umožňují v rámci deklarace specifikovat typ, rozměry a počáteční hodnotu pole a to hned několika způsoby současně.
Inicializace a použití polí
Po deklaraci pole můžeme ke všem prvkům pole přistupovat přes jejich index tak, jako kdyby to byly jednoduché proměnné, tj. můžeme jim nastavovat libovolné hodnoty (u polí typu Object i různého typu) a následně je v kódu používat, přiřazovat a číst, např.:
Dim aPole(99) As Object
aPole(6) = "Ahoj"
aPole(1) = 456.8
System.Console.Write aPole(6)
Skutečná flexibilita polí však vyvstane v případech, že pro index využijeme proměnnou, jejíž hodnotu lze za běhu programu vyhodnotit jako celé číslo - zkuste např. určit hodnotu, kterou vypisuje následující fragment kódu:
Dim i = 1, aPole(9) As Integer
aPole(i - 1) = i + 1
System.Console.Write(aPole(aPole(i)))
Jelikož nastavení každého prvku pole na počáteční hodnotu vyžaduje samostatný příkaz, pro počáteční inicializaci pole prostředí VB.NET nabízí efektivní zkratku, využívající složených závorek, je však použitelná pouze pro dynamicky deklarovaná pole. Např. namísto deklarace a inicializace pole se třemi prvky:
Dim aPole(2)
aPole(0) = "Hello"
aPole(1) = "world"
aPole(2) = 4 * 5
můžeme použít mnohem stručnější dynamickou deklaraci spojenou s inicializací - např. ukázka uvedená níže vypíše hodnotu 20, obsaženou po inicializaci ve třetím prvku pole:
Dim aPole() As Object = {"Hello", "world", 4 * 5}
System.Console.Write(aPole(2))
Prázdné pole inicializujeme pomocí složených závorek bez parametru.
Dim aNull() As Integer = {}
Výhod takového zápisu deklarace se nemusíme vzdávat ani při inicializaci více rozměrných polí - např. ukázka níže deklaruje a inicializuje 3x2x3 pole typu System.Int32():
Dim aSht(,,) As Short = {{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}}, {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}}
Přehledného a současně kompaktního zápisu inicializace i rozměrných polí dosáhneme s využitím faktu, že prostředí VB.NET podporuje zápis příkazů na více řádků s využitím znaku podtržítka, jako znaku pokračování, např.:
Dim a(,) = {{0 + 0, 0 + 1, 0 + 2, 0 + 3}, _
{1 + 0, 1 + 1, 1 + 2, 1 + 3}, _
{2 + 0, 2 + 1, 2 + 2, 2 + 3}, _
{3 + 0, 3 + 1, 3 + 2, 3 + 3}, _
{4 + 0, 4 + 1, 4 + 2, 4 + 3}}
System.Console.Write(a(2, 3))
Zkuste určit výsledek vypisovaný v předchozí ukázce příkazem System.Console.Write()! S použitím takového přehledného zápisu to není nijak obtížný úkol.
Kromě beztypové inicializace lze pole lze inicializovat polem odpovídajícího typu pomocí klíčového slova New. Spolu s typem lze přitom specifikovat i rozměr pole:
Dim aObj() = New String() {"A", "B", "C"}
Dim aInt%() = New Integer(4) {0, 1, 2, 3, 4}
Analogickým způsobem lze inicializovat i vícerozměrná, složená a nepravidelná (jagged) pole, např.:
Dim aArr(2) As System.Array = New Array(2){New Double(2){2, 3.4, 1E-4D}, New Double(3){}, New Double(1){0.2, Math.PI}}
Dim aDbl()() As Double = {New Double(2){0.2, 1, 7}, New Double(1){}}
Dim aSht(,) As Short = New Short(2, 1){{0, 3}, {1, 5}, {2, 6456S}}
Ještě jednou zdůrazněme, že zápis využívající složené závorky {} je použitelný pouze pro dynamicky deklarovaná pole - tj. taková, která nemají v okamžiku inicializace předem definované meze indexů v jednotlivých rozměrech. Fakt, že pole nebylo dosud inicializováno si můžeme za běhu programu ověřit tím, že jej porovnáme s hodnotou Nothing Neinicializované pole nezaměňujme s prázdným polem (nulové délky - viz výše), které má sice nulový počet prvků, ale je inicializováno (alokováno) na haldě a lze je dodatečně předimenzovat příkazem ReDim:
Dim aNull() = {}:
System.Console.WriteLine(aNull Is Nothing)
Procházení pole pomocí smyčky For - Next
Jeden z nejčastějších případů použití konečných smyček For .. Next s čítačem spočívá v procházení proměnných typu pole. Efektivní přístup k jednotlivým prvkům polí představují smyčky For-Next s využitím čítače smyčky v úloze indexů pole, např.:
Const iMAX = 4
Dim aPole(iMAX), i As Integer
For i = 0 to iMAX
aPole(i) = i * i
Console.WriteLine(aPole(i))
Next
Vícerozměrná pole lze efektivně procházet analogickým postupem pomocí odpovídajícího počtu do sebe vnořených smyček For-Next:
Dim aPole(5, 5, 5), i, j, k
For i = 0 to 4
For j = 0 to 4
For k = 0 to 4
aPole(i, j, k) = i + j + k
Next k
Next j
Next i
Ukázka výše provedla inicializaci třírozměrného pole ve třech do sebe vnořených cyklech For - Next. Při procházení pole a jakékoliv práci s jeho indexy pomocí proměnných musíme striktně dbát na to, abychom nepřekročili velikost indexů, nastavených poslední deklarací pole Dim nebo ReDim. V případě, že překročíme v určitém rozměru přípustnou mez pole, prostředí VB.NET vygeneruje chybu - výjimku typu System.IndexOutOfRangeException a činnost programu předčasně ukončí.
Procházení pole pomocí smyčky For - Each
S ohledem na možnost výskytu podobného typu chyb nám prostředí VB.NET nabízí ještě elegantnější způsob procházení prvků pole - a to s využitím smyčky (cyklu) For- Each. Výhodou tohoto způsobu je, že se nemusíme zajímat o velikost jednotlivých rozměrů pole - prostředí VB.NET si je zjistí samo bez ohledu na jejich velikost. V každém kroku cyklu je do řídící proměnné přiřazen jeden z prvků pole. Způsob procházení pole příkazem For - Each nejjednodušeji vyplyne z praktické ukázky, která po proběhnutí všech cyklu vypíše do systémové konzole větu "VB.NET je snadný jazyk."
Dim aSlova() = {"VB.NET ", "je ", "snadný ", "jazyk."}, Slovo
For Each Slovo In aSlova
System.Console.Write(Slovo)
Next
V každém kroku cyklu For-Each dojde k naplnění proměnné Slovo jedním z po sobě následujících prvků pole textových řetězců. Proměnná Slovo zde funguje jako speciální výčtový objekt - tzv. enumerátor - a proto se tento způsob procházení polí někdy nazývá též enumerace (výčet). Přístupu k prvkům pole přes cykly For - Each se nemusíme vzdát ani v případě složitějších polí, neboť příkaz For - Each prochází postupně (sekvenčně) všechny dimenze pole, od vnitřní po vnější v tom pořadí, jak jsou interně uložena v paměti:
Module modMain
Sub Main()
Dim aa(,) = {{0, 1, 2, 3}, _
{1, 2, 3, 4}, _
{2, 3, 4, 5}, _
{3, 4, 5, 6}, _
{4, 5, 6, 7}}, a
For Each a In aa
System.Console.Write(a & " ")
Next a
End Sub
End Module
Ukázka vypíše řadu čísel proložených mezerou, tvořících jednotlivé prvky pole aa().:
0 1 2 3 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7
V souvislosti s cyklem For-Each je nutné upozornit na jeho chování v případě, že pole je tvořeno hodnotovými typy. V tom případě položka cyklu neobsahuje odkaz na příslušný prvek pole, ale (jak je u hodnotových typů obvyklé) na její kopii. Toto chování si můžeme snadno vyzkoušet na následující ukázce:
Imports System.Console
Module modMain
Sub Main
Dim a(5), i As Integer
For i = 0 To 5 ` Pole inicializujeme..
a(i) = i
Next
For Each i In a ` ..zobrazíme obsah pole..
Write(i)
Next
WriteLine()
For Each i In a ` ..nastavíme každý prvek pole..
i = 0
Next
For Each i In a ` ..a opět zobrazíme obsah pole-
Write(i)
Next
End Sub
End Module
Z výpisu ukázky je vidět, že k žádné obsahu pole ve skutečnosti nedošlo. Z uvedeného vyplývá, že procházení pole cyklem For-Each není použitelné, pokud máme v úmyslu měnit prvky pole hodnotového typu a není ani vhodné s ohledem na výkon, pokud je pole rozsáhlé, nebo jej procházíme opakovaně (v každém kroku cyklu se vytváří kopie některého z prvků pole):
012345
012345
Poznámky:
- Jednoduché spojení prvků pole (vč. vícerozměrného) do řetězce bez použití cyklu VB.NET umožňuje provést jedinou funkcí Microsoft.VisualBasic.Join(), resp. metodou System.String.Join(). Jelikož problematika polí je poměrně rozsáhlá, podrobněji si budeme podobnými funkcemi a prací s poli zabývat později při popisu třídy System.Array, která v CRL datový typ pole zastřešuje a z níž jsou všechna pole VB.NET odvozena.
- Stejně jako v cyklu For-Next můžeme verzí.NET 1.1 počínaje při procházení pole cyklem For-Each deklarovat typ řídící proměnné cyklu uvnitř těla cyklu. Deklarace takovéto proměnné má však platnost pouze v rámci daného cyklu:
Dim aInt() As Integer = {1, 2, 3}
For Each i As Integer In aInt
. . .
Next
System.Console.WriteLine(i) ` proměnná i mimo tělo cyklu není deklarována
Stručné shrnutí
Po deklaraci pole lze k jednotlivým prvkům pole přistupovat pomocí indexu/ů podobně jako k obvyklým proměnným. Inicializaci polí lze provést několika způsoby podle toho, zda jsou deklarována staticky, nebo dynamicky. V případě rozměrnějších polí se stává efektivní indexovaný přístup k jednotlivým prvkům pole pomocí vnořených cyklů s čítačem For - Next.. Pokud nepotřebujeme sledovat index pole, lze pole procházet s využitím konstrukce For - Each, kterou lze použít i pro elegantní procházení vícerozměrných polí v jediném cyklu, není však vhodná k procházení polí hodnotového typu.
V dalším dílu se seznámíme podrobněji s rozhraním třídy System.Array a jejími funkcemi, které lze výhodně využít při práci s poli.