STL:n algoritmit¶
STL-kirjasto algorithm
tarjoaa valmiina yleisimmät operaatiot,
joita säiliön sisältämille alkioille on tarpeen tehdä.
Algoritmikirjaston käyttämiseksi on koodiin lisättävä
#include <algorithm>
STL:n valmiit algoritmit ovat geneerisiä: Ne eivät ota kantaa säiliön tyyppiin, vaan ne osaavat operoida millä tahansa säiliöllä.
Käsiteltäväksi haluttu osa säiliön alkioista kerrotaan algoritmifunktiolle iteraattorivälin avulla. Jokaisella funktiolla on aina vähintään kaksi iteraattoriparametria.
Esimerkiksi algoritmi (siis funktio) sort
osaa lajitella
vector
-rakenteen alkiot kasvavaan järjestykseen, kunhan sille
kerrotaan iteraattoreilla lajiteltavaksi haluttu osaväli:
vector<double> reaalivektori;
...
sort(reaalivektori.begin(), reaalivektori.end());
Kannattaa pitää mielessä, että begin
- ja end
-iteraattorien
paikalla mikä tahansa kahden iteraattorin esittämä osaväli on kelvollinen:
vector<double>::iterator valin_eka = reaalivektori.begin();
vector<double>::iterator valin_vika = reaalivektori.end();
++valin_eka; // Osoittaa nyt toiseen alkioon
--valin_vika; // Osoittaa nyt viimeiseen alkioon
// Lajitellaan vektorin sisällä kaikki alkiot
// ensimmäistä ja viimeistä lukuunottamatta.
// Ensimmäinen ja viimeinen pysyvät paikallaan.
sort(valin_eka, valin_vika);
Tärkeä huomio: algoritmikirjaston operaatiot eivät koske
jälkimmäisen iteraattoriparametrin osoittamaan alkioon.
Edellisessä esimerkissä siis viimeinen vektorin alkio ei enää
tullut lajitelluksi, koska valin_vika
osoitti siihen.
Seuraavassa lyhyt lista muista hyödyllisistä algoritmikirjaston funktioista.
Suurimmassa osassa esimerkkirakenteena on vector
, koska se on
tässä vaiheessa ainoa tuttu STL-säliötyyppi.
Kuitenkin, mikäli ei eriksen mainita, funktiot
toimivat muillakin säiliötyypeillä ja merkkijonoilla (string
).
count
laskee säiliössä olevien tietyn arvoisten alkioiden lukumäärän:vector<string> vihamiehet; ... // Kuinka monta Akia vihamiehet-vektorista löytyy? cout << count(vihamiehet.begin(), vihamiehet.end(), "Aki") << " kappaletta Aki-nimisia vihamiehia!" << endl;
count
-funktio ei toimimap
-rakenteella aivan yhtä suoraviivaisesti.min_element
jamax_element
etsivät säiliöstä arvoltaan pienimmän tai suurimman alkion ja palauttavat iteraattorin, joka osoittaa kyseiseen alkioon:vector<int> maarat; ... vector<int>::iterator pienin_it; pienin_it = min_element(maarat.begin(), maarat.end()); cout << "Pienin lukumaara: " << *pienin_it << endl;
min_element
- jamax_element
-funktiot eivät toimimap
-rakenteella aivan yhtä suoraviivaisesti.find
etsii säiliöstä haluttua arvoa ja palauttaa iteraattorin ensimmäiseen löytämäänsä alkioon tai käsiteltävänä olevan säiliönend
-iteraattorin, jos arvoa ei löydy:vector<string> potilaat; ... vector<string>::iterator iter; iter = find(potilaat.begin(), potilaat.end(), "Kai"); if ( iter == potilaat.end() ) { // Ei ole Kaita potilasjonossa. ... } else { // Kai löytyi ja se sijaitsee iter:in osoittamassa // kohdassa: tulostetaan ja poistetaan jonosta. cout << *iter << endl; // vector-säiliöllä on metodi erase, jolla // iteraattorin osoittama alkio voidaan poistaa. potilaat.erase(iter); }
Kannattaa pitää mielessä, että
string
-tietotyypillä ja myöhemmin tutuiksi tulevillaset
- jamap
-rakenteilla on metodifunktio nimeltäfind
, jota kannattaa ehdottomasti käyttää, koska varsinkin kahden jälkimmäisen tapauksessa se on monta kertaluokkaa nopeampi kuin algoritmi-kirjastonfind
.replace
korvaa kaikki halutut arvot uudella arvolla:replace(tekstivektori.begin(), tekstivektori.end(), "TTY", "TUNI");
jossa kaikki vektorissa olevat alkiot, joiden arvo on “
TTY
” korvataan arvolla “TUNI
”.replace
ei toimiset
-rakenteella taimap
-rakenteella.fill
muuttaa kaikki iteraattorivälin alkiot annetun arvoisiksi:string merkkijono = ""; ... // Seuraavan fill-operaation jälkeen koko // merkkijono on täynnä kysymysmerkkejä. fill(merkkijono.begin(), merkkijono.end(), '?');
fill
ei toimiset
-rakenteella taimap
-rakenteella.reverse
kääntää annetun välin takaperoiseen järjestykseen:vector<Pelaaja> vuorojarjestys; ... // Seuraavalla pelikierroksella // vuorojärjestys on käänteinen. reverse(vuorojarjestys.begin(), vuorojarjestys.end());
reverse
ei toimiset
- jamap
-rakenteilla ollenkaan.shuffle
sekoittaa alkiot satunnaiseen järjestykseen:vector<Pelikortti> korttipakka; minstd_rand gen; // Luodaan satunnaislukugeneraattori nimeltä gen ... shuffle(korttipakka.begin(), korttipakka.end(), gen);
shuffle
ei toimiset
- jamap
-rakenteilla ollenkaan.C++:ssa on valmiina määritelty useita erilaisia satunnaislukugeneraattoreita, jotka löytyvät kirjastosta
random
. Tietotyypinminstd_rand
avulla voit luoda yhdenlaisen satunnaislukugeneraattorin ja aiemmin esiintyneendefault_random_engine
-tyypin avulla toisen. Tässä vaiheessa riittää, että osaat luoda näistä yhden (tai molemmat). Halutessasi voit hakea internetistä lisätietoa C++:n erilaisista satunnaislukugeneraattoreista.copy
kopioi säiliön alkiot johonkin toiseen säiliöön:string merkkijono = ""; ... // Alustetaan merkkivektori siten, että siinä on // yhtä monta alkiota kuin merkkijonossa on merkkejä. // Huomaa että tässä on taas tilanne, // jossa alustuksessa pitää käyttää kaarisulkeita. vector<char> merkkivektori(merkkijono.length()); // Kopioidaan kaikki merkkijonon merkit // alkioiksi merkkivektoriin. copy(merkkijono.begin(), merkkijono.end(), merkkivektori.begin());
Kopioinnin kohteessa (siis siellä minne alkiota kopioidaan) pitää olla valmiiksi tilaa tarjolla. Edellä
merkkivektori
on valmiiksi alustettu sisältämään yhtä monta alkiota kuinmerkkijono
:ssa on merkkejä.Tämä pätee kaikkiin STL-algoritmeihin, jotka tallettavat tuloksia säiliöön: Kohdesäiliössä pitää olla valmista tilaa kaikille talletettaville alkioille.
Lähtösäilion ja kohdesäiliön alkioiden tyyppien pitää olla samat.
Useista algorithm
-kirjaston funktioista on olemassa versio, jonka
toimintaa voidaan säätää funktioparametrin avulla.
Otetaan tässä esimerkkinä sort
-funktio.
Ennen varsinaista asiaa, on tärkeä ymmärtää, että sort
-funktio ei
voi lajitella suuruusjärjestykseen kuin sellaista tietoa, jolle on
määritelty jonkinlainen suuruusrelaatio. Tämä ei päde esimerkiksi
vektorille, jonka alkiot ovat värejä, sillä ei ole ollenkaan itsestäänselvää,
onko purppura pienempi vai suurempi kuin vaikkapa violetti, koska
väreillä ei ole suuruusjärjestystä.
Tämä ei ole ongelma silloin, kun lajiteltavassa säiliössä on alkioina kielen perustyyppistä tietoa, merkkijonoja tai joitakin muita C++:ssa valmiiksi määriteltyjä tyyppejä. Mutta heti, jos yritetään käsitellä omatekemiä tyyppejä (luokkia), joita ei pystytä automaattisesti vertailemaan, törmätään ongelmiin.
Tutkitaan pieni esimerkki (tilan säästämiseksi epäolennaisia koodirivejä jätetty pois):
class Opiskelija {
public:
Opiskelija(string const& nimi, int opnum);
string hae_nimi() const;
int hae_opnum() const;
void tulosta() const;
private:
string nimi_;
int opnum_;
};
bool vertaile_opiskelijanumeroita(Opiskelija const& op1,
Opiskelija const& op2) {
return op1.hae_opnum() < op2.hae_opnum();
}
bool vertaile_nimia(Opiskelija const& op1,
Opiskelija const& op2) {
return op1.hae_nimi() < op2.hae_nimi();
}
int main() {
vector<Opiskelija> opiskelijat = {
{ "Teekkari, Tiina", 121121 },
{ "Arkkari, Antti", 111222 },
{ "Fyysikko, Ville", 212121 },
{ "Teekkari, Teemu", 100011 },
{ "Kone, Kimmo", 233233 },
};
// Järjestetään nousevaan järjestykseen opiskelijanumeroiden perusteella
// ja tulostetaan
sort(opiskelijat.begin(), opiskelijat.end(), vertaile_opiskelijanumeroita);
for ( auto opisk : opiskelijat ) {
opisk.tulosta();
}
cout << string(30, '-') << endl;
// Järjestetään nousevaan järjestykseen nimen perusteella
// ja tulostetaan
sort(opiskelijat.begin(), opiskelijat.end(), vertaile_nimia);
for ( auto opisk : opiskelijat ) {
opisk.tulosta();
}
}
Esimerkin funktiot vertaile_opiskelijanumeroita
ja
vertaile_nimia
ovat nk. vertailufunktioita:
- Parametrit (2 kpl) ovat vakioviitteitä sen tietotyypin alkioihin, joita funktion on tarkoitus vertailla.
- Paluuarvon tyyppi on
bool
. - Paluuarvo on
true
täsmälleen silloin, kun ensimmäinen parametri on aidosti pienempi kuin toinen parametri.
Vertailufunktion voi antaa lisäparametrina sellaisille algoritmikirjaston funktioille, joiden toiminta riippuu käsiteltävänä olevan säiliön alkioiden suuruudesta.
Näin ollen edellisen esimerkin vektorista voitaisiin hakea ja tulostaa opiskelijanumeroltaan suurin opiskelija:
vector<Opiskelija>::iterator iter;
iter = max_element(opiskelijat.begin(),
opiskelijat.end(),
vertaile_opiskelijanumeroita);
// Koska seuraava tulosta-metodi kohdistetaan
// iteraattorin osoittamaan olioon, käytetään
// -> -operaattoria pisteen sijaan.
iter->tulosta();
Kun vertailufunktio annetaan parametrina algoritmikirjaston funktiolle, kutsukohdassa käytetään pelkkää funktion nimeä. Jos funktion perässä olisi kaarisulkeet, yrittäisi kutsua funktiota ja antaa parametrina vertailufunktion paluuarvon, mikä ei ole tarkoitus.
Funktioita, jotka saavat parametrina toisen funktion, kutsutaan korkeamman kertaluvun funktioiksi (higher-order function).