Lekce 8 - Pole v C# .NET
V minulé lekci kurzu, Ošetření uživatelských vstupů v C# .NET, jsme si ukázali ošetření uživatelských vstupů.
Dnes si v C# .NET tutoriálu představíme datovou strukturu pole a vyzkoušíme si, co všechno umí.
Pole
Představme si, že si chceme uložit nějaké údaje o více prvcích.
Např. chceme v paměti uchovávat 10 čísel, hrací pole šachovnice nebo
jména 50 uživatelů. Asi nám dojde, že v programování bude nějaká
lepší cesta než začít bušit proměnné uzivatel1
,
uzivatel2
až např. uzivatel50
. Nehledě na to, že
jich může být třeba 1000. Jak by se pak navíc v takovém systému hledalo?
Brrr, takto ne
Potřebujeme-li uchovávat větší množství proměnných stejného
typu, tento problém nám řeší pole. Pole si můžeme představit
jako řadu přihrádek, kdy v každé máme uložený jeden prvek. Přihrádky
jsou očíslované tzv. indexy, přičemž první přihrádka má index
0
.
(Na obrázku je vidět pole osmi čísel.)
Programovací jazyky se velmi liší v tom, jak s polem pracují. V některých jazycích (zejména starších, kompilovaných) nebylo možné za běhu programu vytvořit pole s dynamickou velikostí (např. mu dát velikost dle nějaké proměnné). Pole se muselo deklarovat s konstantní velikostí přímo ve zdrojovém kódu. Toto se obcházelo tzv. pointery a vlastními datovými strukturami, což často vedlo k chybám při manuální správě paměti a k nestabilitě programu (např. v C++). Naopak některé interpretované jazyky umožňují nejen deklarovat pole s libovolnou velikostí, ale dokonce tuto velikost na již existujícím poli měnit (např. PHP). My víme, že C# je virtuální stroj, tedy cosi mezi kompilerem a interpretem. Proto můžeme pole založit s velikostí, kterou dynamicky zadáme až za běhu programu, ale velikost již existujícího pole už modifikovat nemůžeme. Toto lze samozřejmě obejít nebo použít jiné datové struktury, ale k tomu se dostaneme později.
Možná vás napadá, proč se tu polem zabýváme, když má evidentně mnoho omezení a existují lepší datové struktury. Odpověď je prostá: pole je totiž jednoduché. Nemyslíme tím pouze pro nás na pochopení, ale zejména pro samotný jazyk C#. S polem se rychle pracuje, protože prvky jsou v paměti jednoduše uloženy za sebou, všechny zabírají stejně místa a rychle se k nim přistupuje. Mnoho vnitřních funkčností v .NET proto nějak pracuje s polem nebo pole vrací. Jedná se tedy o klíčovou strukturu.
Pro hromadnou manipulaci s prvky pole se používají cykly.
Pole deklarujeme pomocí hranatých závorek:
int[] pole;
Na české klávesnici jsou hranaté závorky ukryté pod Pravým Alt společně s klávesami F a G:
Výraz pole
je samozřejmě název naší proměnné. Nyní jsme
však pouze deklarovali, že v proměnné bude pole prvků typu
int
. Nyní musíme pole založit, abychom ho mohli používat.
Použijeme k tomu klíčové slovo new
, které na
naší úrovni znalostí nejsme zatím schopní podrobně vysvětlit. Je to
kvůli tomu, že pole je referenční datový typ, který
budeme probírat v navazujícím kurzu Objektově
orientovaného programování v C# .NET:
int[] pole = new int[10];
Nyní máme v proměnné pole
pole o velikosti deseti prvků
typu int
.
K prvkům pole potom přistupujeme přes hranatou závorku. Pojďme nyní na
první index (tedy index 0
) uložit číslo 1
.
int[] pole = new int[10]; pole[0] = 1;
Plnit pole takto ručně by bylo příliš pracné, proto použijeme cyklus a
naplníme si pole čísly od 1
do 10
. K naplnění
použijeme for
cyklus:
int[] pole = new int[10]; pole[0] = 1; for (int i = 0; i < 10; i++) pole[i] = i + 1;
Abychom pole vypsali, můžeme za předchozí kód připsat:
{CSHARP_CONSOLE}
int[] pole = new int[10];
pole[0] = 1;
for (int i = 0; i < 10; i++)
pole[i] = i + 1;
for (int i = 0; i < pole.Length; i++)
Console.Write("{0} ", pole[i]);
{/CSHARP_CONSOLE}
Všimněme si, že pole má vlastnost Length
, kde je uložena
jeho délka, tedy počet prvků.
Konzolová aplikace
1 2 3 4 5 6 7 8 9 10
Můžeme použít zjednodušenou verzi cyklu pro práci s kolekcemi, známou
jako foreach
. Tento cyklus projede všechny prvky v poli a jeho
délku si zjistí sám. Jeho syntaxe je následující:
foreach (datovytyp promenna in kolekce) { // příkazy }
Cyklus projede prvky v kolekci (což je obecný název pro struktury, které obsahují více prvků, u nás to bude pole) postupně od prvního do posledního. Prvek máme v každé iteraci cyklu uložený v dané proměnné.
Také pro tento cyklus existuje ve Visual Studiu užitečný snippet:
napíšeme foreach
a dvakrát stiskneme Tabulátor,
zbytek cyklu se sám dopíše. Zkuste si to.
Přepišme tedy náš dosavadní program pro foreach
. Cyklus
foreach
nemá řídicí proměnnou, není tedy
vhodný pro vytvoření našeho pole a použijeme ho jen pro výpis.
{CSHARP_CONSOLE}
int[] pole = new int[10];
pole[0] = 1;
for (int i = 0; i < 10; i++)
pole[i] = i + 1;
foreach (int i in pole)
Console.Write("{0} ", i);
Console.ReadKey();
{/CSHARP_CONSOLE}
Výstup programu:
Konzolová aplikace
1 2 3 4 5 6 7 8 9 10
Pole samozřejmě můžeme naplnit ručně, a to i bez toho, abychom dosazovali postupně do každého indexu. Použijeme k tomu složené závorky a prvky oddělujeme čárkou:
string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};
Pole často slouží k ukládání mezivýsledků, které se potom dále v programu používají. Když potřebujeme nějaký výsledek 10x, tak ho nebudeme 10x počítat, ale spočítáme ho jednou a uložíme ho do pole, odkud poté výsledek jen načteme.
Metody na třídě Array
.NET nám poskytuje třídu Array
, která obsahuje pomocné
metody pro práci s poli. Pojďme se na ně podívat:
Sort()
Jak již název napovídá, metoda nám pole seřadí. Její jediný parametr je pole, které chceme seřadit. Metoda je dokonce tak chytrá, že pracuje podle toho, co máme v poli uložené. Stringy třídí podle abecedy, čísla podle velikosti. Zkusme si setřídit a vypsat naši rodinku Simpsonů:
{CSHARP_CONSOLE}
string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};
Array.Sort(simpsonovi);
foreach (string s in simpsonovi)
Console.Write("{0} ", s);
Console.ReadKey();
{/CSHARP_CONSOLE}
Konzolová aplikace
Bart Homer Lisa Maggie Marge
Zkuste si udělat pole čísel a vyzkoušejte si, že to opravdu funguje i pro ně.
Reverse()
Reverse()
nám pole otočí (první prvek bude jako poslední
atd.). Toho můžeme využít např. pro třídění pozpátku:
{CSHARP_CONSOLE}
string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};
Array.Sort(simpsonovi);
Array.Reverse(simpsonovi);
foreach (string s in simpsonovi)
Console.Write("{0} ", s);
Console.ReadKey();
{/CSHARP_CONSOLE}
IndexOf()
a
LastIndexOf()
Tyto metody vrátí index prvního nebo posledního nalezeného prvku. V
případě nenalezení prvku metody vrátí -1
. Každá z metod
bere dva parametry: prvním je pole, druhým hledaný prvek. Umožníme
uživateli zadat jméno Simpsona a řekneme mu, na které pozici je uložený.
Teď to pro nás nemá hlubší význam, protože prvek pole je jen
string
. Bude se nám to však velmi hodit ve chvíli, kdy v poli
budeme mít uloženy plnohodnotné objekty. Následující příklad tedy berme
jako takovou přípravu.
{CSHARP_CONSOLE}
string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};
Console.WriteLine("Ahoj, zadej svého oblíbeného Simpsona (z rodiny Simpsonů): ");
string simpson = Console.ReadLine();
int pozice = Array.IndexOf(simpsonovi, simpson);
if (pozice >= 0)
Console.WriteLine("Jo, to je můj {0}. nejoblíbenější Simpson!", pozice + 1);
else
Console.WriteLine("Hele, tohle není Simpson!");
Console.ReadKey();
{/CSHARP_CONSOLE}
Konzolová aplikace
Ahoj, zadej svého oblíbeného Simpsona (z rodiny Simpsonů):
Homer
Jo, to je můj 1. nejoblíbenější Simpson!
Copy()
Copy()
již podle názvu zkopíruje část pole do jiného pole.
Prvním parametrem je zdrojové pole, druhým cílové pole a třetím počet
prvků, který se má zkopírovat.
Metody na poli
Třída Array
není jedinou možností, jak s polem manipulovat.
Přímo na samotné instanci pole (konkrétní proměnné) můžeme volat také
spoustu metod. I když si zmíníme jen některé, je jich opravdu hodně.
Nebudeme tedy uvádět příklady, metody si pouze popíšeme:
Length
Length
jsme si již zmínili, vrátí délku pole. Není
metodou, ale vlastností, proto se za ni nepíšou závorky ()
.
Následující metody jsou takzvané "extension metody".
Podrobněji je probereme později. V tuto chvíli musíme jen vědět, že pro
jejich zavolání potřebujeme balíček:
using System.Linq;
. Kód using System.Linq;
si
dopíšeme pod: using System;
).
Min()
, Max()
,
Average()
, Sum()
Matematické metody, jež vracejí nejmenší prvek (Min()
),
největší prvek (Max()
), průměr ze všech prvků
(Average()
) a součet všech prvků (Sum()
). Metody
nemají žádné parametry.
Concat()
,
Intersect()
, Union()
Všechny tyto metody vrátí na výstupu nové pole a jako parametr mají
druhé pole. Concat()
vykoná nám již známou konkatenaci, tedy k
našemu poli připojí druhé pole a takto vzniklé nové pole vrátí.
Intersect()
vykoná průnik obou polí, tedy sestaví pole s prvky,
které jsou oběma polím společné. Union()
naopak vykoná
sjednocení, funguje tedy podobně jako Concat()
, pouze prvky,
které byly v obou polích, jsou v novém poli jen jednou.
First()
a Last()
Již podle názvu metody vrátí první a poslední prvek, neberou žádné parametry.
Take()
a Skip()
Obě tyto metody berou jako parametr počet prvků. Take()
vrátí pole s daným počtem prvků zkopírovaných od začátku původního
pole. Skip()
naopak vrátí pole bez těchto prvních prvků.
Contains()
Metoda vrací true
/false
podle toho, zda se prvek
uvedený v parametru metody v daném poli nachází.
Reverse()
Metodu Reverse()
známe již z třídy Array
. Pokud
ji však voláme na konkrétním poli, tak se prvky v něm neotočí, nýbrž je
vytvořeno nové otočené pole a to je vráceno. Metoda nemá žádné
parametry.
Distinct()
Distinct()
je metoda bez parametrů a zajistí, aby byl v poli
každý prvek jen jednou, tedy vymaže duplicitní prvky a unikátní pole
vrátí jako návratovou hodnotu metody, opět tedy nemodifikuje dané pole.
Mnoho metod nemění přímo naše pole, ale pouze vrátí pole nové (jsou
to metody Concat()
, Intersect()
, Union()
,
Reverse()
a Distinct()
), ve kterém jsou provedeny
požadované změny. Pokud chceme modifikovat původní pole, musíme do něj
dosadit hodnoty. Zmíněné metody bohužel z důvodů, které pochopíme až
později, nevracejí přímo pole, ale typ IEnumerable
. Aby bylo
dosazení výsledku zpět do pole možné, musíme ho ještě převést na pole
metodou ToArray()
.
int[] cisla = { 1, 2, 3, 3, 3, 5 }; cisla = cisla.Distinct().ToArray();
Proměnná délka pole
Říkali jsme si, že délku pole můžeme definovat i za běhu programu. Pojďme si to zkusit a rovnou si vyzkoušejme nějakou metodu na poli:
Console.WriteLine("Ahoj, spočítám ti průměr známek. Kolik známek zadáš?"); int pocet = int.Parse(Console.ReadLine()); int[] cisla = new int[pocet]; for (int i = 0; i < pocet; i++) { Console.Write("Zadejte {0}. číslo: ", i + 1); cisla[i] = int.Parse(Console.ReadLine()); } Console.WriteLine("Průměr tvých známek je: {0}", cisla.Average()); Console.ReadKey();
Konzolová aplikace
Ahoj, spočítám ti průměr známek. Kolik známek zadáš?
5
Zadejte 1. číslo: 1
Zadejte 2. číslo: 2
Zadejte 3. číslo: 2
Zadejte 4. číslo: 3
Zadejte 5. číslo: 5
Průměr tvých známek je: 2,6
Tento příklad by bylo samozřejmě možné napsat i bez použití pole, ale co kdybychom chtěli spočítat např. medián? Nebo např. vypsat zadaná čísla pozpátku? To už by bez pole nešlo. Takto máme v poli k dispozici původní hodnoty a můžeme s nimi neomezeně a jednoduše pracovat.
To by pro dnešek stačilo, můžete si s polem hrát.
V následujícím kvízu, Kvíz - Podmínky, cykly, pole v C# .NET, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 1439x (141.34 kB)
Aplikace je včetně zdrojových kódů v jazyce C#