Práce na poli

Pole hodnot

autor: Ing. Petr Pecháček
officir@ic.cz
http://officir.ic.cz

Sešit Excelu s příklady (komprese ZIP)

Na úvod

Pole slouží k seskupování hodnot téhož typu, na které se později odkazujeme indexem (pořadovým číslem). Svým charakterem je podobný maticím. (Pozn. Pokud jednotlivé položky nejsou stejného typu, je třeba použít uživatelský datový typ.)

Deklarace polí

Statické pole

Pole deklarujeme podobně jako proměnné v tom smyslu, že je uvozujeme slovy Dim, Public, Private, Static. Dále následuje jméno pole a stanovení mezí (volitelně dolní_mez To horní_mez) a nakonec přichází známé As a typ hodnot. Statické pole je přitom takové, u něhož definujeme vždy alespoň jednu z mezí. Jednou definované meze statického pole nelze již dále měnit. Příklady napoví více.

Statické pole

Pole mohou mít i více rozměrů. Dvojrozměrné pole celých čísel ukazuje další obrázek. Dané pole pojme 5×10, tedy 50 čísel.

2D pole

Dolní mez nemusíme stanovit. Zde je příklad pole, jehož horní mez (maximální pořadové číslo položky) je 3:

Dim Pole(3) As String
V tomto případě si vždy musíte položit otázku, jak je to s dolní mezí. Devadesát procent lidí, kteří nemají co do činění s programováním, odpoví, že dolní index (dolní mez) je roven jedné. Osobně to považuji za logické... Bohužel, pokud není řečeno jinak, pak obzvláště zahraniční autoři i vývojáři berou za samozřejmé, že dolní mez je rovna nule. To znamená, že počet hodnot v poli je pak o jednu vyšší. Aby dolní mez byla skutečně rovna jedné, musíte použít příkaz Option Base 1 hned v úvodu modulu, nebo jasně stanovit dolní mez. Pozn. Příkaz Option Base 0 má také za následek dolní index nula. Pamatujte ovšem, že explicitně definovaná dolní mez "přebije" příkaz Option Base.
Dolní mez pole je 0Dolní mez pole je 1
Nevhodná deklarace

Dynamické pole a příkaz ReDim

Leckdy nebudeme předem znát velikost (meze) pole. Takové pole si tedy nadefinujeme dynamicky a někde v kódu později použijeme příkaz ReDim (změna velikosti horní meze pole bez zachování původních hodnot), častěji pak ReDim Preserve (změna velikosti horní meze pole se zachováním původních hodnot). Použití příkazu ReDim u dynamicky definovaného pole je před jeho prvním naplněním každopádně nutností!

Sub DynamickePole()
    Dim Pole()
    M = 3
    'Redimenzování je nutné před dalším zpracováním
    'Redimenzování s výmazem položek
    ReDim Pole(1 To M)
    'Redimenzování se zachováním položek
    ReDim Preserve Pole(1 To M)
End Sub

Pozor! Ačkoliv v případě ReDim Preserve mluvíme o zachování stávajícího obsahu, pokud pole zmenšíme, dojde k odstranění "přetékajícího" obsahu. Příkaz ReDim Preserve neumožňuje změnu počtu rozměrů pole ani změnu dolní meze.

Pozn.:
Deklarace může mít podobu Dim Pole i Dim Pole(). Možné je obojí. Úplné vymezení pole zajistí příkaz ReDim později v kódu. Uvedené závorky ale přeci jen říkají "pozor, bude to pole".

Závorky v deklaraci pole

Naplnění pole

Jednoduché (ale neefektivní) je "ruční" naplnění pole běžným přiřazením.

Sub NaplneniPole()
    Dim Pole(1 To 3)
    Pole(1) = "šroub"
    Pole(2) = "matka"
    Pole(3) = "podložka"
End Sub

Programové naplnění vyžaduje použití cyklu a s trochou vtipu se uplatní i tam, kde byste jej nečekali.

Pole měsíců

Mnohem častější bude naplnění pole z oblasti buněk listu, kdy se rovněž uplatní klasický cyklus. Zde je důležité si uvědomit podobnost pole a matice hodnot.

Načtení oblasti do 1D pole

Pozn. Obrázek nezdůrazňuje jednu podstatnou věc. Jednorozměrné pole, tak, jak je definováno, odpovídá řádkové matici (v listu Excelu by položky maticového vzorce byly oddělené středníkem). Na to musíme myslet, pokud budeme chtít obsah jednorozměrného pole kopírovat do sloupcové oblasti buněk. Pole můžeme převracet pomocí WorksheetFunction.Transpose (česky Transpozice - viz maticový počet).

Transpozice pole
Více příkladů na dané téma naleznete v přiloženém sešitu a navíc se oblastem z listu budeme věnovat v příštím článku.

Funkce Array

Funkce Array nám pomáhá naplnit pole a zkracuje množství potřebného kódu.

Sub FunkceArray()
    Dim Pole()
    Pole = Array("šroub", "matka", "podložka")
    'indexování závislé na Option Base
End Sub

V některé litaratuře se dočteme, že dolní mez pole vytvořeného funkcí Array je vždy nula bez ohledu na příkaz Option Base. Podle mých zkušeností (a v souladu s nápovědou) je dolní mez na příkazu Option Base závislá. Bez hlubšího vysvětlení si také pamatujte, že u pole definovaného funkcí Array neuvádíme typ hodnot pole (například Dim Pole As String) - došlo by k chybě za běhu programu. Funkce se také nesnáší se statickou definicí pole - viz příklad.

Sub ChybnaDefinicePole()
   Dim Pole(1 To 3)
   'taktéž Dim Pole(0 To 2)
   Pole = Array("šroub", "matka", "podložka")
   'funkce Array neumí naplnit takto definované pole
End Sub

Tip: Seznam příbuzných položek pro funkci Array často nechávám vygenerovat v listu Excelu a k sloučení položek používám vlastní funkci OFFRETEZ (je součástí doplňku Offset Universal - viz webové stránky). Na stránkách Officíra také najdete sešit "prorady.xls", který již časté seznamy obsahuje (čísla, měsíce, jména, prvočísla, ...).

Hledání v poli

Předně musím konstatovat, že efektivní metody pro práci s polem chybí. Neexistuje žádná metoda či vlastnost Search nebo Exists (o stupeň lépe je na tom kolekce Dictionary). Jako první se tak nabízí prosté procházení položek v cyklu a kontrola položky po položce. Při desítkách položek (i stovkách) je tento přístup ještě akceptovatelný, jsou-li položek tisíce, pak už musíme pátrat po algoritmech věnujících se třídění a vyhledávání. To je ovšem téma na samostatnou kapitolu, ba přímo knihu. V přiloženém sešitu naleznete ukázky vyhledávání jak v setříděném, tak nesetříděném poli.

Další práce na poli...

Kopie pole

Kopírování polí je snadná záležitost, jen si pamatujte, že běžně je možné kopírovat pouze dynamicky definovaná pole. U statických polí bychom museli sáhnout po API funkci CopyMemory (viz přiložený sešit).

Sub PrirazeniPolePoli()
    Dim Pole1()
    Dim Pole2()
    Pole1 = Array(10, 2, 15, 125)
    Pole2 = Pole1
End Sub

Netradiční použití dvou polí

Méně typické použití dvou polí ukazuje následující příklad, který má charakter jednoduché číselné šifry.

Netradiční použití dvou polí

Příkaz Erase

Výsledek této funkce se liší pro staticky a dynamicky definované pole. V případě statického pole resetuje obsah jeho položek, ve druhém případě zcela odstraní obsah pole z paměti. Zkrátka, v obou případech dochází k nové inicializaci pole. Přitom platí, že příkaz ReDim (bez Preserve), který zachovává velikost pole, má u dynamického pole stejný výsledek jako funkce Erase u statického pole.

Funkce Filter

Funkce Filter, jak napovídá její název, umí filtrovat pole obsahující textové řetězce. Příklad viz níže. Detail funkce najdete v nápovědě.
Funkce Filter

Jak bylo řečeno, funkce pracuje s textovými položkami a naneštěstí neumí zpracovat korektně číselné hodnoty (explicitně definované číselné pole přímo odmítá). Jejím výsledkem je vždy pole typu String. Podívejte se na obrázek.

Nekorektní funkce Filter

Funkce Split

Tato funkce umí vytvořit pole z řetězce oddělených položek.

Funkce Split

Funkce Join

Funkce Join je pravým opakem funkce Split. Z pole vytváří řetězec oddělených položek

Řetězec z pole

Bezmezné úvahy

Algoritmy, které pracují s poli, jsou někdy postaveny na polích s indexováním od nuly, někdy od jedné. Lze tvořit univerzální algoritmy? Převážně ano. Meze analyzujeme pomocí funkcí LBOund(Pole) (dolní mez) a Ubound(Pole) (horní mez). A protože pole bohužel nedisponují vlastností Count pro zjištění počtu položek, dopočítáme se jich nezávisle na Option Base následovně:

PocetPolozek = UBound(Pole) - LBound(Pole) + 1

Jak se odkazovat vždy na n-tou položku pole bez ohledu na Option Base?

n = 2
OdkazNaEntouPolozku = Pole(n - 1 + LBound(Pole))

Na závěr

Některé programovací jazyky nabízí kromě pole ještě proměnné typu zásobník a fronta, které jsou poli podobné. Názvy už napovídají, o co jde. V případě zásobníku jde jakoby skutečně o zásobník, do kterého vkládáme náboje a přitom platí, že poslední vložený náboj vyjde ze zbraně jako první (zkratka LIFO, tj. Last In First Out). Naproti tomu, kdo je první ve frontě, je jako první obsloužen (FIFO, tj. First In First Out). U obou typů se tak počítá s posunem obsahu proměnných a to je právě věc, která poli chybí. Naprogramovat pole, aby fungovalo jako zásobník či fronta, vyžaduje hlubší zamyšlení.

Sešit Excelu s příklady (komprese ZIP)

Officír pro časopis CHIP.