Lekce 7 - Pole ve Swift
V minulé lekci kurzu, Typový systém: Optionals ve Swift, jsme se naučili používat Optionals.
Dnes si ve Swift tutoriálu představíme datovou strukturu pole a vyzkoušíme si, co všechno umí.
Pole
Představte si, že si chcete uložit nějaké údaje o více prvcích.
Např. chcete v paměti uchovávat 10 čísel, políčka šachovnice nebo jména
50 uživatelů. Asi vám dojde, že v programování bude nějaká lepší
cesta, než začít bušit proměnné uzivatel1
,
uzivatel2
... až uzivatel50
. Nehledě na to, že jich
může být třeba 1000. A jak by se v tom potom hledalo? Brrr, takhle ne
Pokud potřebujeme uchovávat větší množství proměnných
stejného typu, tento problém nám řeší pole. Můžeme si ho
představit jako řadu přihrádek, kde v každé máme uložený jeden prvek.
Přihrádky jsou očíslované tzv. indexy, první 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 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). Swift k polím přistupuje poměrně moderně. Neřeší velikost (ani ji nemůžete pevně zadat) a pro přidávání prvků se používá metoda, takže nemusíte řešit indexy. Tomuto typu kolekce (kontejnerové struktury) se v některých jazycích říká spíše seznam, než pole. Swift jej ovšem jako pole označuje. Pro hromadnou manipulaci s prvky pole se používají cykly.
Pole deklarujeme pomocí hranatých závorek a obyčejných závorek:
var pole = [Int]()
Výraz pole
je samozřejmě název naší proměnné.
K prvkům pole potom přistupujeme přes hranatou závorku, ale pouze, pokud
již tento prvek existuje. To může být matoucí, pokud přicházíte z
jiného jazyka. Prvky nejdříve přidáme metodou append()
, které
můžeme v našem případě předat číslo (Int
, který se
uloží na konec pole) nebo klidně další pole stejného typu (tedy typu
Int
v našem případě).
var pole = [Int]() pole.append(34)
Plnit pole takto ručně by bylo příliš pracné, použijeme cyklus a
naplníme si pole čísly od 1
do 10
. K naplnění
použijeme for
cyklus:
var pole = [Int]() for i in 1...10 { pole.append(i) }
Abychom pole vypsali, můžeme za předchozí kód připsat:
{SWIFT}
var pole = [Int]()
for i in 1...10 {
pole.append(i)
}
for cislo in pole {
print(cislo, terminator:" ")
}
{/SWIFT}
Nyní už je lépe vidět síla jiného zápisu for
cyklu ve
Swiftu. Ostatní jazyky mívají další foreach, nám ale stačí za
in
místo definované řady čísel vložit pole a cyklus projde
všechny prvky, ke kterým se v jeho těle můžeme dostat.
1 2 3 4 5 6 7 8 9 10
Pole má vlastnost count
, kde je uložen počet prvků.
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 hranatých závorek a prvky oddělujeme čárkou:
let simpsonovi = ["Homer", "Marge", "Bart", "Lisa", "Maggie"]
Protože nechceme, aby nám někdo obsah pole měnil, deklarujeme ho pomocí
let
a tím se pole stává konstantní. Vynechali jsme upřesnění
typu, protože Swift snadno pozná, že jde o pole stringů. Samozřejmě nám
ale nic nebrání ho doplnit:
let simpsonovi : [String] = ["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 to nebudeme 10x počítat, ale spočítáme to jednou a uložíme do pole, odtud poté výsledek jen načteme.
Metody dostupné pro pole
Swift nám poskytuje pomocné metody pro práci s poli. Pojďme se na ně podívat:
sort()
a sorted()
Jak již název napovídá, metody nám pole seřadí. Metoda
sort()
setřídí existující pole (takže musí být deklarované
s var
) a sorted(
) nám vrátí nové setříděné
pole, takže ho nesmíte zapomenout přiřadit do proměnné. Metody jsou
dokonce tak chytré, že pracují podle toho, co máme v poli uložené. Hodnoty
typu String
se třídí podle abecedy, čísla podle velikosti.
Zkusme si setřídit a vypsat naši rodinku Simpsnů:
{SWIFT}
var simpsonovi = ["Homer", "Marge", "Bart", "Lisa", "Maggie"]
simpsonovi.sort()
for simpson in simpsonovi {
print(simpson)
}
{/SWIFT}
Výstup:
Bart Homer Lisa Maggie Marge
A způsob seřazení pomocí metody sorted()
:
{SWIFT}
var simpsonovi = ["Homer", "Marge", "Bart", "Lisa", "Maggie"]
let simpsonovi_sorted = simpsonovi.sorted()
for simpson in simpsonovi_sorted {
print(simpson)
}
{/SWIFT}
Výstup:
Bart Homer Lisa Maggie Marge
Zkuste si udělat pole čísel a vyzkoušejte si, že to opravdu funguje i pro ně.
reverse()
a
reversed()
Tyto metody nám pole otočí (první prvek bude jako poslední atd.).
Princip je opět stejný jako u třídění. Metoda reverse()
setřídí existující pole, reversed()
nám potom seřazené pole
vrátí. Toho můžeme využít např. pro třídění pozpátku:
{SWIFT}
var simpsonovi = ["Homer", "Marge", "Bart", "Lisa", "Maggie"]
simpsonovi.sort()
simpsonovi.reverse()
for simpson in simpsonovi {
print(simpson)
}
{/SWIFT}
Výstup:
Marge Maggie Lisa Homer Bart
index(of: )
Vrací index daného prvku. Metoda nám vrátí Optional(Int)
,
protože se může stát, že prvek v poli prostě neexistuje a v takovém
případě nemá smysl vracet jakoukoliv hodnotu. Optionals byste měli znát z
předchozí lekce. Abychom se k možné hodnotě dostali bezpečně, použijeme
konstrukci if let
, která do proměnné pozice přiřadí hodnotu
indexu Simpsna, pokud takový v našem poli existuje. Jestliže neexistuje, tak
se provede else
větev.
{SWIFT}
let simpsonovi = ["Homer", "Marge", "Bart", "Lisa", "Maggie"]
print("Ahoj, zadej svého oblíbeného Simpsna (z rodiny Simpsnů): ");
let simpson = readLine()!
if let pozice = simpsonovi.index(of: simpson) {
print("Jo, to je můj \(pozice + 1). nejoblíbenější Simpson!")
} else {
print("Hele, tohle není Simpson!")
}
{/SWIFT}
Výstup:
Ahoj, zadej svého oblíbeného Simpsna (z rodiny Simpsů): Homer Jo, to je můj 1. nejoblíbenější Simpson!
Kopírování pole
Občas se může stát, že z nějakého důvodu potřebujete přesnou kopii
vašeho pole. To je ve Swiftu buď velmi jednoduché a nebo poměrně
zdlouhavé. Záleží totiž na tom, jestli se vaše pole skládá z
hodnotových typů (Int
, Double
, String
)
nebo referenčních (instance tříd, o těch se podrobně pobavíme až v
dalších Swift kurzech). V prvním případě stačí vytvořit novou
proměnnou pole a to stávající do něj přiřadit. Použijeme naše pole
Simpsonů, které se skládá z hodnot typu String
- tedy
hodnotových typů a vytvoříme jeho kopii.
var simpsonovi = ["Homer", "Marge", "Bart", "Lisa", "Maggie"] var kopieSimpsnu = simpsonovi
Případ s referenčními datovými typy zatím nebudeme řešit.
count
count
jsme si již zmínili, obsahuje délku pole. Není
metodou, ale vlastností, nepíší se za ni tedy závorky ()
.
isEmpty
Jak asi tušíte, tato vlastnost vrátí true
, pokud je naše
pole prázdné. Je to lepší, než se ptát, jestli je počet
(count
) roven nule a z kódu je hned jasné, že nás zajímá
možná prázdnost pole.
min()
a max()
Matematické metody, vracející nejmenší prvek (min()
),
největší prvek (max()
). Výsledek vrátí jako
Optional
, pro případ, že by pole bylo prázdné.
first
a last
Již podle názvu vlastnosti vrátí první a poslední prvek.
contains()
Metoda vrací true
/false
podle toho, zda se prvek,
uvedený v parametru metody, v daném poli nachází.
Mazání prvků
Moderní provedení pole ve Swiftu nám umožňuje pohodlně mazat prvky.
remove(at: )
Metoda remove()
odstraní z pole prvek na námi zadaném indexu.
Musíme si ale dát pozor, aby index skutečně existoval, jinak kód skončí
chybou. Častěji asi budete chtít z pole smazat konkrétní prvek místo prvku
na vybraném indexu. Swift bohužel nenabízí jednoduchou metodu, která toto
zajistí, můžeme si ale pomoci metodou index(of: )
, kterou již
známe:
if let bartIndex = simpsonovi.index(of: "Bart") { simpsonovi.remove(at: bartIndex) }
Takto můžeme z našeho pole Simpsnů smazat Barta. Myslete na to, že pole
musí být deklarováno jako var
, protože konstantní přes
let
nám nedovolí úpravy. Metoda remove(at: )
smazaný prvek zároveň vrátí, pokud byste s ním chtěli pracovat.
removeAll()
Zde není moc co vysvětlovat. Metoda jednoduše z pole odstraní všechny
prvky. Dostupná je ještě varianta s parametrem keepingCapacity
.
Pole si totiž na pozadí drží kapacitu pro prvky a v případě potřeby se
zvětšuje. Pokud máte v poli mnoho prvků, chcete je odstranit, ale víte, že
jej brzy naplníte dalšími v podobném počtu, můžete Swiftu ušetřit
práci s opětovným budováním kapacity. Není to ale nic důležitého a
klidně používejte removeAll()
bez parametru.
removeLast()
a
popLast()
Obě metody odstraní poslední prvek v poli a vrátí nám ho. Jak se tedy
liší? Zásadně v případě, že je pole prázdné. removeLast()
v takovém případě vyvolá chybu, zatímco popLast()
jednoduše
vrátí prázdný Optional
. Pokud víte, že pole určitě nebude
prázdné, bude jednoduší využít removeLast()
, protože
nemusíte řešit rozbalování Optional
.
Tyto metody se hodí také v případě, že chcete pomocí pole emulovat
datovou strukturu zásobník (Stack), kterou Swift oproti dalším jazykům
nenabízí. Její princip je prostý. Funguje jako pomyslný štos papírů na
stole. Nové prvky (papíry) vždy přijdou navrch a při odebírání se
nejdříve dostaneme k těm vrchním - tedy těm, které byly přidány
posledně. Pomocí append()
metody, která přidává na konec pole
a těchto removeLast()/popLast()
, jednoduše docílíte stejného
chování.
To by pro dnešek stačilo, můžete si s polem hrát.
V následujícím cvičení, Řešené úlohy k 7. lekci Swift, si procvičí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 21x (104.8 kB)
Aplikace je včetně zdrojových kódů v jazyce Swift