11. díl - Vícerozměrná pole v C# .NET

C# .NET Základní konstrukce Vícerozměrná pole v C# .NET American English version English version

V minulém tutoriálu o základech C# .NET jsme si uvedli metody Split() a Join() na textových řetězcích. Dnešní díl je v sekci základních konstrukcí C# .NET v podstatě bonusový a pojednává o tzv. vícerozměrných polích. Teoreticky můžete rovnou přejít k objektově orientovanému programování, doporučuji však si konec této sekce ještě alespoň projít, abyste měli o zbývajících technikách povědomí, přeci jen se jedná o dosti základní vědomosti.

Již umíme pracovat s jednorozměrným polem, které si můžeme představit jako řádku přihrádek v paměti počítače.

Struktura pole

(Na obrázku je vidět pole osmi čísel)

Ačkoli to není tak časté, v programování se občas setkáváme i s vícerozměrnými poli a to zejména pokud programujeme nějakou simulaci (např. hru).

Dvourozměrné pole

Dvourozměrné pole si můžeme v paměti představit jako tabulku a mohli bychom takto reprezentovat např. rozehranou partii piškvorek. Pokud bychom se chtěli držet reálných aplikací, které budete později v zaměstnání tvořit, můžeme si představit, že do 2D pole budeme ukládat informace o obsazenostech sedadel v kinosálu. Situaci bychom si mohli graficky znázornit např. takto:

Struktura dvourozměrného pole

(Na obrázku je vidět 2d pole reprezentující obsazenost kinosálu)

Kinosál by byl v praxi samozřejmě větší, ale jako ukázka nám toto pole postačí. 0 znamená volno, 1 obsazeno. Později bychom mohli doplnit i 2 - rezervováno a podobně. Pro tyto stavy by bylo správnější vytvořit si vlastní datový typ, tzv. výčet, ale s ním se setkáme až později, takže si teď musíme vystačit pouze s čísly.

2D pole deklarujeme v C# .NET následujícím způsobem:

int[,] kinosal = new int [5, 5];

První číslice udává počet sloupců, druhá počet řádků (samozřejmě si to můžeme určit i obráceně, např. matice v matematice se zapisují opačně).

Všechna číselná pole v C# .NET jsou po deklaraci automaticky inicializována samými nulami, můžeme se na to spolehnout. Vytvořili jsme si tedy v paměti tabulku plnou nul.

Naplnění daty

Nyní kinosál naplníme jedničkami tak, jak je vidět na obrázku výše. Protože budeme jako správní programátoři líní, využijeme k vytvoření řádku jedniček for cykly :) Pro přístup k prvku 2D pole musíme samozřejmě zadat 2 souřadnice.

kinosal[2, 2] = 1; // Prostředek
for (int i = 1; i < 4; i++) // 4. řádek
{
        kinosal[i, 3] = 1;
}
for (int i = 0; i < 5; i++) // Poslední řádek
{
        kinosal[i, 4] = 1;
}

Výpis

Výpis pole opět provedeme pomocí cyklu, na 2d pole budeme potřebovat cykly 2 (jeden nám proiteruje sloupce a druhý řádky). Jako správní programátoři nevložíme počet řádků a sloupců do cyklů napevno, jelikož se může změnit. C# .NET poskytuje na 2D poli vlastnost Length jako tomu bylo u 1D pole, ale ta vrací celkový počet prvků v poli, v našem případě tedy 25. Nás bude zajímat metoda GetLength(), která přijímá jako parametr dimenzi (0 sloupce, 1 řádky) a vrátí nám počet prvků v této dimenzi. První dimenzí je počet sloupců, druhou počet řádků.

Cykly zanoříme do sebe tak, aby nám vnější cyklus projížděl řádky a vnitřní sloupce v aktuálním řádku. Po výpisu řádku je nutné odřádkovat. Oba cykly musí mít samozřejmě jinou řídící proměnnou:

for (int j = 0; j < kinosal.GetLength(1); j++)
{
        for (int i = 0; i < kinosal.GetLength(0); i++)
        {
                Console.Write(kinosal[i, j]);
        }
        Console.WriteLine();
}

Výsledek:

Výpis 2D pole v C# .NET

N-rozměrná pole

Někdy může být příhodné vytvořit si pole o ještě více dimenzích. My všichni si jistě dokážeme představit minimálně 3D pole. S příkladem s kinosálem se nabízí případ užití, kdy má budova více pater (nebo obecně více kinosálů). Vizualizace by vypadala asi nějak takto:

3D pole v C# .NET

3D pole můžeme vytvořit tím samým způsobem, jako 2D pole:

int[, ,] kinosaly = new int [5, 5, 3];

Kód výše vytvoří 3D pole jako na obrázku. Přistupovat k němu budeme opět přes indexer (hranaté závorky) jako předtím, jen již musíme zadat 3 souřadnice.

kinosaly[3, 2, 1] = 1; // Druhý kinosál, třetí řada, čtvrtý sloupec

Pokud metodě GetLength() zadáme parametr s hodnotou 2, získáváme počet "pater" (kinosálů).

Pole polí

Mnoho programovacích jazyků vlastně vícerozměrná pole nepodporuje, C# je spíše výjimkou. Můžeme si v nich ale stejně vytvořit kolika-rozměrné pole chceme, jelikož 2D pole není vnitřně nic jiného, než pole polí. Situaci si můžeme představit tak, že si vytvoříme pole o pěti prvcích (1. řádek) a každá buňka v tomto řádku v sobě bude obsahovat další pole, reprezentující sloupeček.

Takové 2D pole deklarujeme následujícím způsobem:

int[][] kinosal = new int[5][];

Výhodou takto deklarovaného 2D pole je fakt, že si do každého řádku/sloupce můžeme uložit jak velké pole chceme. V některých případech tedy nemusíme "plýtvat" pamětí na celou tabulku a můžeme pole vytvořit "zubaté" (anglicky jagged):

Pole polí v C# .NET

Nevýhodou tohoto přístupu je, že musíme pole nepříjemně inicializovat sami. Původní řádek s pěti buňkami sice existuje, ale jednotlivé sloupečky si do něj musíme navkládat sami (zatím si vložme všechny sloupečky o 5ti prvcích):

for (int i = 0; i < kinosal.Length; i++)
{
        kinosal[i] = new int[5];
}

C# rovněž dále neposkytuje žádný komfort ve formě získání počtu sloupců a řádků polí polí. Velikost pole musíme získat takto:

int sloupcu = kinosal.Length;
int radku = 0;
if (sloupcu != 0)
        radku = kinosal[0].Length;

Všimněte si, že je nutné ptát se na počet sloupců, pokud je totiž 0, nemůžeme se dostat k 1. sloupci, abychom zjistili jeho délku (počet řádků ve sloupci).

K hodnotám v poli poté přistupujeme pomocí 2 indexerů:

kinosal[4][2] = 1; // Obsazujeme sedadlo v 5. sloupci a 3. řadě

(Použití jediného indexeru nám vrátí celý sloupeček na daném indexu)

Zkrácená inicializace vícerozměrných polí

Ještě si ukážeme, že i vícerozměrná pole je možné rovnou inicializovat hodnotami (kód vytvoří rovnou zaplněný kinosál jako na obrázku):

int[,] kinosal = new int[,] {
        { 0, 0, 0, 0, 1 },
        { 0, 0, 0, 1, 1 },
        { 0, 0, 1, 1, 1 },
        { 0, 0, 0, 1, 1 },
        { 0, 0, 0, 0, 1 }
};

(Pole je v tomto zápisu otočené, jelikož definujeme sloupce, které zde zapisujeme jako řádky).

Podobnou inicializaci můžeme použít dokonce i u polí zubatých (kód níže vytvoří zubaté pole jako na obrázku):

int[][] zubatePole = new int[][] {
        new int[] {15, 2, 8, 5, 3},
        new int[] {3, 3, 7},
        new int[] {9, 1, 16, 13},
        new int[] {},
        new int[] {5}
};

Na závěr bych rád dodal, že někteří lidé, kteří neumí správně používat objekty, využívají 2D polí k ukládání více údajů o jediné entitě. Např. budeme chtít uložit výšku, šířku a délku pěti mobilních telefonů. Ačkoli se vám nyní může zdát, že se jedná o úlohu na 3D pole, ve skutečnosti se jedná o úlohu na obyčejné 1D pole (přesněji seznam) objektů typu Telefon. Ale o tom až u objektově orientovaného programování. Pole si můžete vyzkoušet ještě v cvičení v této sekci.

Příště se podíváme na matematické funkce a základní seriál zakončíme.


 

Stáhnout

Staženo 85x (32.73 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (4)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (8 hlasů) :
55555


 


Miniatura
Předchozí článek
Cvičení k 10. lekci C# .NET
Miniatura
Všechny články v sekci
Základní konstrukce jazyka C#

 

 

Komentáře

Avatar
patrik.valkovic
Šéfredaktor
Avatar
patrik.valkovic:

Jen bych rád doplnil, že někdy se také využívá klasického jednorozměrného pole pro simulaci pole vícerozměrného. Často jsem se s tím setkal napříkald v C.
Idea je taková, že index dopočítáme. Vezneme příklad : mám krychli o hraně 5 prvků. Jednorozměrné pole bude mít 555=125 prvků.
Pokud budeme chtít získat prvek v hloubce 3 (z), 2 prvek zhora (y) a 4 prvek od levého kraje (x), můžeme se dopočítat následovně.

int IndexPrvku = (z-1) * Vyska(5) * Sirka(5) + (y-1) * Sirka(5) + x;

To platí za předpokladu, že indexujeme z levého horního rohu směrem do pravého dolního a teprve potom dozadu. Jedná se tedy o 59 prvek. To, že je prvek ve třetí vrstvě znamená, že před ním jsou ještě dvě vrstvy, které musíme dopočítat, proto se odečítá jednička.
Jedná se jen o zajímavost, se kterou se ale často v C setkávám :)

Odpovědět  +1 13.7.2015 16:26
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
patrik.valkovic
Šéfredaktor
Avatar
patrik.valkovic:

Omlouvám se, použil jsem hvězdičky místo krát a tak z toho mám místo násobení kurzívu. Má tam být samozřejmě "Jednorozměrné pole bude mít 5 krát 5 krát 5=125 prvků."
Už příspěvek nemůžu upravit

Odpovědět 13.7.2015 17:59
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Martin Turner:

Zdravím,
mohl bych požádat o radu, jak zjistit indexy daného prvku ve vícerozměrném poli?
Děkuji.

 
Odpovědět 30.7.2015 10:20
Avatar
Odpovídá na Martin Turner
Štefan Pružinský:

Ahoj,
riešenie tvojho problému je jednoduché. Predstav si takéto pole:

1   2   3   4   5
6   7   8   9   10
11  12  13  14  15
16  17  18  19  20
21  22  23  24  25
26  27  28  29  30
31  32  33  34  35

A chceme nájsť napr. index čísla 29:

int hladanyPrvok = 29;
for (int y = 0; y < pole.GetLength(1); y++)
{
    for (int x = 0; x < pole.GetLength(0); x++)
    {
        if (pole[x, y] == hladanyPrvok)
            Console.Write(x + "x" + y);
    }
}

Výpis je 3x5, čiže x = 3 a y = 5 (Pole začína od čísla nula. Ak chceme index ôs, ktoré začínajú od 1, k výsledku prirátame 1 tzn. 4x7). Algoritmus je nasledovný: Prejdeme všetky prvky poľa a pri každom prvku overujeme jeho zhodu z hľadaným prvkom. :)

Odpovědět 31.7.2015 14:03
Najefektívnejším spôsobom debuggingu je modlitba. :)
Avatar
Martin Turner:

Děkuji,

dospěl jsem postupně ke stejnému výsledku. :-)

 
Odpovědět  +1 31.7.2015 14:13
Avatar
Ondřej Krsička
Redaktor
Avatar
Ondřej Krsička:

To zapisování sloupečků na řádek je strašně stupidní.

int[,] kinosal = new int[,] {
        { 0, 0, 0, 0, 1 },
        { 0, 0, 0, 1, 1 },
        { 0, 0, 1, 1, 1 },
        { 0, 0, 0, 1, 1 },
        { 0, 0, 0, 0, 1 }
};
 
Odpovědět 13. května 15:17
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.