Razred Krog

Razred Krog

Avtor: Andrej Koželj

Učni cilji: prikazati bralcu kodiranje razreda in nekaj preprostih metod v jeziku C#

Povzetek

V tem gradivu si bomo pogledali izdelavo preprostega razreda, ki omogoča predstavitev kroga kot objekta razreda Krog. Krog bomo lahko predstavili kot niz, lahko ga bomo shranili na datoteko, izračunali njegovo ploščino, obseg, preverili, če je nek krog vsebovan v drugem in še kaj.

Sam sem kodo napisal v okolju Visual Studio (2010), vendar je to vplivalo samo na dodajanje reference na knjižnico z razredom, ko sem testiral metode. Povsem dovolj je urejevalnik besedila (beležnica, wordpad) in pa prevajalnik za izvorne datoteke, napisane v jeziku C#.

Ideja programa

Najprej je potrebno povedati, kaj bi radi pravzaprav vključili v naš razred.

Najprej nas zanima, kako bomo podali podatke, ki so potrebni za prikaz kroga. Ko bomo imeli napisan razred, bi to izgledalo nekako takole:

Krog a=new Krog(<podatki>);

krog lahko predstavimo na več načinov. Lahko podamo koordinate središča in radij, lahko podamo koordinate kvadrata, ki vsebuje (mu je včrtan) ta krog, lahko podamo koordinate kvadrata, ki mu je ta krog očrtan (poteka skozi oglišča kvadrata)...

Odločimo se, da bomo krog predstavili v obliki niza "x;y;r", kjer sta x in y koordinati središča, r pa radij kroga. To pomeni, da bomo takšen niz podali, ko bomo naredili nov objekt tipa Krog. V samem razredu bodo podatki predstavljeni drugače, in sicer, s tabelo dolžine 2, ki vsebuje koordinati kroga in pa s spremenljivko radij. Prazen konstruktor (metoda, ki nam inicializira objekt) bo ustvaril objekt, ki ima središče v (0,0) in radij 0. Konstruktor s parametrom niz pa bo ustvaril krog s koordinatama (x,y) in radijem r.

Če predpostavimo, da naš uporabnik vnese samo lepe in veljavne podatke, lahko to napišemo takole:

public double radij;
public double[] koordinate;
public Random rd;
public Krog(string niz)
{
    koordinate=new double[2];
    string[] nizi=niz.Split(';');
    radij=double.Parse(nizi[2]);
    koordinate[0]=double.Parse(nizi[0]);
    koordinate[1]=double.Parse(nizi[1]);

Seveda v resnici vse to v resnici najprej preverimo, da se nam ob nesmiselnih podatkih pojavi ustrezna izjema. posebej naredimo tudi prazen konstruktor (brez parametra niz).

Metode za izpis

Če želimo naš objekt prikazati na uproabniku razumljiv način, moramo seveda dodati tudi nekaj metod za izpis objekta.

Za to dodamo dve metodi. Prva nam omogoča izpis objekta kot niza. Za format niza uporabimo kar enakega, kot ga podamo ob konstrukciji objekta, se pravi "x;y;r". To je naša nova metoda ToString(), napišemo pa to takole:

public override string ToString()
        {
            return koordinate[0]+";"+koordinate[1]+";"+radij;
        }

S tem lahko sedaj izpisujemo naše objekte v konzolo ali pa tudi kam drugam. Če imamo naprimer objekt razreda krog z imenom krog1, lahko napišemo

Console.WriteLine(krog1.ToString());

in krog se nam izpiše v konzoli.

Da nam ni potrebno tega pisati za vsak objekt posebej, napišemo tudi metodo, ki naš objekt izpiše v tem formatu še na neko datoteko, ki jo podamo. Da ne ustvarjamo vsakič nove datoteke, ali pa preberemo celotne datoteke, jo prepišemo v pomnilnik in ponovno izpišemo v datoteko, niz dodajamo v načinu File.Append; to nam omogoča, da naš niz preprosto dodamo na konec datoteke.

S to metodo naš krog lahko sicer izpišemo na katerokoli nezaščiteno datoteko, vendar pa je verjetno smiselno,d a imamo datoteko, na kateri so izključno takšni nizi. Kasneje bomo namreč dodali še kakšno metodo, ki pričakuje datoteko v takšnem formatu. Koda za dodajanje na datoteko pa izgleda takole:

public void DodajNaDatoteko(string pot)
        {
            TextWriter datoteka;
            if (!File.Exists(pot))//podana pot ne obstaja
            {
                datoteka = new StreamWriter(pot);
                datoteka.Close();
            }
            using (StreamWriter dat = File.AppendText(pot))
            {
                dat.WriteLine(this.ToString());
            }
        }

Branje datoteke

Sedaj, ko imamo že metodo za izpis kroga na datoteki, bi nam prišla prav še kakšna metoda, ki datoteko prebere in nam vrne izpisane kroge. Tako dodamo še metodo PreberiDatoteko(string dat), ki nam iz krogov, izpisanih na datoteki dat ustvari tabelo, ki jih vsebuje. To tabelo potem vrnemo kot rezultat metode. Glede na to, da sprožimo izjemo že v konstruktorju, če podatki niso veljavni, predpostavljamo, da so na datoteki izpisani izključno krogi v primernem formatu. Za vsako vrstico torej ustvarimo nov objekt Krog(vrstica) in ga dodamo v tabelo, ki jo definiramo na začetku metode.

Koda za to metodo izgleda nekako takole:

public Krog[] PreberiZDatoteke(string pot)
        {
            string[] vrstice = File.ReadAllLines(pot);
            Krog[] tab = new Krog[vrstice.Length];
            int stevec;
            for (stevec = 0; stevec < tab.Length; stevec++)
            {
                tab[stevec] = new Krog(vrstice[stevec].Trim());
            }
            return tab;
        }

Ta metoda nam vrne tabelo krogov. Če pa hočemo vsebino datoteke samo izpisati, ker bi radi na hitro preverili kakšno drugo metodo, pa je lahko uporabna tale koda:

public void IzpisiDatoteko(string pot)
        {
            TextReader dat = new StreamReader(pot);
            foreach (string vrstica in File.ReadAllLines(pot))
            {
                Console.WriteLine(vrstica.Trim());
                System.Threading.Thread.Sleep(200);
            }
            dat.Close();
        }

Vsebovanost v drugem krogu

Radi bi napisali tudi metodo, ki nam pove, če je nek objekt krog vsebovan v drugem objektu istega tipa. Naj spomnim, krog je vsebovan v nekem drugem krogu, če:

  • je ves vsebovan v krogu
  • je ves vsebovan v krogu in se ga dotika
  • ga popolnoma pokriva (ima enako središče in radij kot drugi krog)

Preveriti moramo torej samo, če je vsota razdalje med središči in radija vsebovanega kroga manjša ali enaka radiju vsebujočega kroga. V tem primeru je prvi vsebovan v drugem.

To lahko napišemo zelo enostavno, z uporabo knjižnice Math:

public bool Vsebovan(Krog krog)
        {
            double razlikaSredisc = Math.Sqrt(Math.Pow(Math.Abs(this.koordinate[0] - krog.koordinate[0]), 2);
            razlikaSredisc+= Math.Pow(Math.Abs(this.koordinate[1] - krog.koordinate[1]), 2));
            if (radij + razlikaSredisc <= krog.radij)
            {
                return true;
            }
            return false;
        }

Zaporedje

Za konec bi radi napisali še metodo, ki prebere neko datoteko, na kateri so izpisani krogi, in poišče najdaljše zaporedje krogov v tej datoteki. Zaporedje v tem primeru pomeni, da je vsak naslednji člen zaporedja, ki je objekt razreda Krog, vsebovan v prejšnjem členu. Naprimer, če imamo krog, ki vsebuje drug krog, ta pa še tretjega, je to zaporedje dolžine 3.

Ideja moje rešitve je v tem, da poiščemo mesto začetka in pa dolžino tega zaporedja. Tako lahko zaporedje najdemo z zgolj enim pregledom krogov.

Datoteko najprej preberemo, in tako dobimo tabelo krogov. če je prazna, naredimo novo prazno datoteko. V nasprotnem primeru pa definiramo štiri spremenljivke; indeks začetka najdaljšega zaporedja, dolžino najdaljšega zaporedja, indeks začetka trenutnega zaporedja in pa dolžino najdaljšega zaporedja. Nato samo preverjamo po dva kroga med seboj.

Na začetku najdaljše zaporedje določimo kot mesto 0 in z dolžino 1. To pomeni, da je trenutno najdaljše zaporedje prvi element tabele. Nato se premaknemo na trenutno zaporedje. Če je naslednji krog vsebovan v prejšnjem, dolžino zaporedja povečamo za 1, mesto pa ostane enako. To ponavljamo, dokler naslednji krog ni vsebovan v prejšnjem. Če imamo sedaj dolžino zaporedja večjo od dolžine najdaljšega, indeks in dolžino najdaljšega zaporedja zamenjamo s trenutnim.

Iz tega lahko vidimo, da, če v datoteki obstaja več najdaljših zaporedij, dobimo prvo. Če bi pogoj > spremenili na >=, bi dobili zadnje najdaljše zaporedje.

Koda za to metodo sledi na naslednji prosojnici.

Zaporedje-koda

Koda metode Zaporedje:

public void Zaporedje(string vhod, string izhod)
        {
            Krog[] tab = PreberiZDatoteke(vhod);//tabela krogov na datoteki
            if (tab.Length == 0)//datoteka je prazna, zato ustvarimo prazno novo datoteko
            {
                TextWriter dat = new StreamWriter(izhod);
                dat.Close();
                return;
            }
            int prejsnjeZaporedje = 0;//indeks začetka trenutnega najdaljšega zaporedja
            int dPrejsnjega = 1;//dolžina trenutno najdaljšega zaporedja
            int trenutnoZaporedje = 0;//indeks začetka trenutnega zaporedja
            int dTrenutnega = 1;//dolžina trenutnega zaporedja
            int mesto = 1;//trenutno mesto, na katerem se nahajamo
            while (mesto < tab.Length)//sprehodimo se po tabeli krogov
            {
                if (tab[mesto - 1].Vsebovan(tab[mesto]))//zaporedje podaljšamo
                {
                    dTrenutnega++;
                }
                else//naredimo novo zaporedje, prej shranimo kot najdaljše, če je daljše od zadnjega najdaljšega
                {
                    if (dTrenutnega > dPrejsnjega)
                    {
                        dPrejsnjega = dTrenutnega;
                        prejsnjeZaporedje = trenutnoZaporedje;
                    }
                    trenutnoZaporedje = mesto;
                    dTrenutnega = 1;
                }
                mesto++;
            }
            if (dTrenutnega > dPrejsnjega)
            {
                dPrejsnjega = dTrenutnega;
                prejsnjeZaporedje = trenutnoZaporedje;
            }
            TextWriter dat1 = new StreamWriter(izhod);
            for (mesto = prejsnjeZaporedje; mesto < prejsnjeZaporedje + dPrejsnjega; mesto++)//zapišemo zaporedje na datoteko izhod
            {
                dat1.WriteLine(tab[mesto]);
            }
            dat1.Close();
        }

Ostalo

Sedaj pa še nekaj enostavnih metod, ki sem jih dodal razredu Krog:

Izračun ploščine

public double Ploscina()
        {
            return Math.PI*Math.Pow(radij,2);
        }

Izračun obsega

public double Obseg()
        {
            return 2 * Math.PI * radij;
        }

Izpis krogov, ki nimajo središča v (0,0)

public void Izpisi(Krog[] tab)
        {
            int stevec = 0;//preštejemo kroge, ki nimajo središča v (0,0)
            foreach (Krog k in tab)
            {
                if (!(k.koordinate[0] == 0 && k.koordinate[1] == 0))
                {
                    stevec++;
                }
            }
            Krog[] novaTab = new Krog[stevec];//naredimo novo tabelo, v kateri bodo samo krogi, ki nimajo središča v izhodišču
            stevec = 0;
            foreach (Krog k in tab)
            {
                if (!(k.koordinate[0] == 0 && k.koordinate[1] == 0))
                {
                    novaTab[stevec] = k;
                    stevec++;
                }
            }
            Array.Sort(novaTab, delegate(Krog k1, Krog k2)//uredimo tabelo glede na velikost krogov
            {
                return k1.radij.CompareTo(k2.radij);
            });
            foreach (Krog k in novaTab)
            {
                Console.WriteLine(k.ToString());
            }
        }

Filmček-testni primeri

Za konec pa še Wink filmček, ki prikazuje nekaj testnih primerov.

0%
0%