Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++ multimap ja sort

TsaTsaTsaa [07.12.2007 11:41:39]

#

struct Otus
{
   string nimi;
   int numero;
};

typedef multimap< int, Otus* > otusmap;

int main()
{
   // jotakin ....
   otusmap otukset;
   // Laitetaan kaikenmoisia alkioita mappiin
   sort(otukset.begin(), otukset.end(), nimijarj);
}


// Järjestämisen olisi tarkoitus mennä niin, että samalla avaimella olevat
// järjestettäisiin nimen mukaan.
bool nimijarj( /* mitä parametriksi? */ )
{
   return ......;
}

Lähinnä ongelma on, että minkä tyyppiset parametrit tuolle vertailufunktiolle pitää antaa?

Pekka Karjalainen [07.12.2007 13:10:24]

#

Multimappia ei voi järjestää uudelleen. Sen toiminta perustuu sisäiseen järjestykseen, koska se käyttää tasapainotettua binääripuuta.

Yksi tapa saada haluamasi tulos on käyttää seuraavanlaista rakennetta:

map <int, vector < Otus* > >;

Lisätessäsi uusia Otuksia mappiin haet avainta vastavan vektorin ja teet siihen push_back-operaation. Kun haluat järjestyksen, käyt mapin läpi ja järjestät jokaisen vektorin erikseen.

TsaTsaTsaa [07.12.2007 13:24:46]

#

Jeps, sitten vielä semmoinen kysymys, että miten tuollaisesta vektorista sitten haetaan? Siis kun pitää poistaa vektorista tietty alkio.

map<int, vector< Otus* >> mappi;
vector< Otus* >::iterator haku = find(mappi[5].begin(), mappi[5].end(), hakufunktio);
mappi[5].erase(haku);

Minkälainen hakufunktion tulee olla, jos haluaa tietyn nimisiä aina hakea?

neau33 [07.12.2007 16:48:48]

#

Moikka! TsaTsaTsaa

pomppaa tänne

TsaTsaTsaa [07.12.2007 17:51:48]

#

Köh...tuosta nyt ei ollut mitään apua ja nyt puhutaan jo vectorista. Lähinnä siis ongelmana nuo findissa ja sortissa käytettävät vertailu- sun muut funktiot.

Pekka Karjalainen [07.12.2007 19:24:57]

#

Ensinnäkin, varoitan, että koodini on kirjoitettu saunassa käynnin jälkeen ja liian pikaisesti. Se kelpaa g++:lle -Wall-optiolla, mutta voi muuten olla hieman huonoa johtuen post-saunallisesta fiiliksestä. Yksi kohta siinä on oleellinen, josta kohta tarkemmin lisää.

Voisi myös sanoa, että jos kokoelmat yhden avaimen kohdalla ovat isoja, voit harkita toisen säiliön käyttö vektorin sijasta. Tämä vain siinä tapauksessa, että vektorin käyttö todella hidastaa ohjelmaasi.

Jos haluat hakea kokoelmasta alkiota jonkin ehdon avulla, käytät find_if-funktiota. Se ottaa argumentiksi välin alun ja lopun, sekä jonkin predikaatin, jota testaamalla alkio ehkä löytyy. Predikaatti on tässä funktio, joka ottaa argumentiksi yhden kokoelman alkion ja palauttaa bool-tyyppisen tiedon, joka kertoo, toteuttaako alkio ehdon.

Tässä on esimerkki, miten sen voi määritellä luokkana, jonka funktiokutsuoperaattori on yliladattu. Find_if ymmärtää muitakin funktion tapaisia asioita, kuten osoittimia funktioihin.

class Otuksen_Nimen_Yhdensuuruus {
	private:
		string _nimi;
	public:
		bool operator()(Otus& otus) {
			return _nimi == otus.nimi();
		}
		Otuksen_Nimen_Yhdensuuruus (string nimi) : _nimi (nimi) { /* ... */ }
};

Private-kentällä nyt ei tässä ole merkitystä, kun luokka on näin pieni. Mutta tulipa tehtyä kaavan mukaan. Oleellisena erona ohjelmaasi on, että yksinkertaisuuden vuoksi en käytä osoittimia.

Tällainen olio luodaan find_if-kutsun yhteydessä antamalla nimi, jota sen pitää testata. Se näyttää tältä:

Otuksen_Nimen_Yhdensuuruus ("Pertti")

Koska näiden testifunktioiden teko jokaiselle eri oliolle on tylsää, voi käyttää templatea helpottamaan tilannetta.

Tästä minulla on esimerkkiohjelma:

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

using std::string;

class Otus {
	private:
		string _nimi;
		int _nro;
	public:
		string nimi() { return _nimi;}
		void nimi (string uusi_nimi) { _nimi = uusi_nimi;}
		int nro () {return _nro;}
		void nro (int uusi_nro) { _nro = uusi_nro;}
		Otus (string, int);
};

Otus::Otus (string nimi, int nro) : _nimi(nimi), _nro(nro)  { /* ... */}

template <typename T>
class Nimen_Yhdensuuruus {
	private:
		string _nimi;
	public:
		bool operator()(T& nimekas_olio) {
			return _nimi == nimekas_olio.nimi();
		}
		Nimen_Yhdensuuruus (string nimi) : _nimi (nimi) { /* ... */ }
};

void tulosta (std::vector <Otus>& tulostettavat) {
	using namespace std;
	for (vector<Otus>::iterator i = tulostettavat.begin();
		i != tulostettavat.end(); ++i)
			cout << i -> nimi() << " " << i -> nro ()<< endl;
}

int main (void) {
	std::vector <Otus> otukset;
	otukset.push_back (Otus ("vekkuli", 3));
	otukset.push_back (Otus ("turjake", 5));
	otukset.push_back (Otus ("hapsenkakkiainen", 11));
	otukset.push_back (Otus ("suhnutti", 2));
	std::cout << "Poistoa ennen" << std::endl;
	tulosta (otukset);
	std::vector <Otus>::iterator k = find_if (otukset.begin(), otukset.end(),
		Nimen_Yhdensuuruus<Otus>("turjake"));
	if (k != otukset.end()) otukset.erase (k);
	std::cout << "Poiston jälkeen" << std::endl;
	tulosta (otukset);
}

Huomaa, että find_if:in jälkeen testaan, ettei k osoita vektorin loppuun, minkä find_if palauttaa, jos se ei löydä yhtään predikaatin toteuttavaa alkiota.

Omassa ohjelmassasi voit varmasti nimetä apuluokan paremmin. On tuo vähän turhan pitkä nimi -- ja lisäksi yleensä koodaan englanniksi.

Ja noista käyttämistäni setter- ja getter-metodeista ei tarvitse pitää. Jotkut koodausstandardit suosivat toisia tapoja, ja niin voit tehdä sinäkin.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta