Gyakorlati alapok II.

Utóirat: a korrekt római szám validátor

www.informatika-programozas.hu - Ez a programozási feladat nehéz lesz!

 

A római számkijelzésben van valamiféle belső, döcögős logika, de természetesen mégsem oly következetes, mint az arab számábrázolás. Éppen ezért olyan nehéz megállapítani belső összefüggéseit, ám most mégis erre kényszerülünk, ha korrekt római szám validátort kívánunk implementálni.

 

www.informatika-programozas.hu - Ezt most meg kell tanulni!

 

A validálás tehát ebben (mint sok más esetben) azt jelenti, hogy ellenőrizzük a bevitt-bekért karaktersorozatok, tágabb értelemben bármilyen bemeneti adat formai-tartalmi helyességét. Szinte szabványos függvénytípusa boolean, amelynek végső visszatérése értéke (return) true vagy false lehet. A honlapon már sokszor találkozhattunk ilyen jellegű validáló függvénnyel; legtöbbször a boolean rosszAdat = false értékadásból indultunk ki, amelyet a validálás true értékre állíthatott át.

 

Római számok esetében a következő szempontok alapján kell validálnunk:

A validálás szinte szabványos műveletei a következők lesznek:

A fenti validáló szempontokat nézzük meg szétbontva, pszeudokód formájában is!

 

Bevitt karakterek szerint

Ha nem I vagy V vagy X vagy L vagy C vagy D vagy M

 

Digitszám (karakterszám) mennyisége szerint

Ha karakterszám 0 vagy karakterszám < 1 vagy karakterszám > 9

 

Digitszám pozíciója szerint

Konkrét karakterkombinációk szerint

Először nézzük meg elméleti síkon:

Mivel itt konkrét, egyúttal statikus karakterkombinációkról beszélhetünk, érdemes őket egyetlen statikus tárolási szerkezetbe, mondjuk tömbbe rendezni. Ekkor a belső logikai struktúrákat is (IIII és nagyobb) konkrét karakterkombinációkká bontjuk ki. Rossz beviteli karakterkombinációk a következők:

Mivel a római szám validálás láthatóan bonyolult ügy, ezért elképzelhető, hogy a fenti szempontok között lesz redundáns funkcionalitás. (Utólagos megjegyzés: később kiderült, hogy egyértelműen lett.) Ez azonban -ha a fenti szempontok helyesek-, nem lesz probléma, azaz nem fog rossz végeredményt szolgáltatni. Nyilvánvalóan itt az egyetlen nem kívánatos eredmény csakis az lehet, ha a boolean rosszAdat értéke true értékre lesz állítva, miközben ez valójában nem igaz.

 

Mivel a romaiSzamValidator() függvényt egyetlen metódusként tervezzük, a fenti szempontok jól szegmentáltan, mintegy "almetódusokként" fognak üzemelni. Semmi más dolgunk nem lesz, mint funkciójukat teljesítve meghatározzák, hogy a boolean rosszAdat változó true vagy false állapotban van-e.

 

A romaiSzamValidator() függvénynek egyetlen, String típusú bemeneti adata lesz (boolean romaiSzamValidator(String romaiString)).

 

Ugyanakkor megállapíthatunk egyfajta validálási sorrendet is az almetódusok között. Ekkor az általánosabb hibától haladunk az egyre specifikusabb hibákig:

  1. ha nem történt karakterbevitel,

  2. ha a római szám hossza kisebb 1-nél vagy nagyobb 9-nél,

  3. ha rossz karakterbevitel történt,

  4. ha rossz karakterkombináció bevitele történt (ez a funkció további, elég bonyolult almetódusokra bontott).

A keletkezett almetódusok a következők:

 

static boolean romaiSzamValidator(String romaiString){
    rosszAdat = false;
    rosszAdat = hossz(romaiString);
    rosszAdat = karakter(romaiString);
    rosszAdat = nemMegengedettString(romaiString);
    rosszAdat = nemMegengedettString2(romaiString);
    rosszAdat = karakterPozicio(romaiString);
    rosszAdat = karakterPozicio2(romaiString);
    return rosszAdat;
}
 

hossz()

A romaiString hosszának ellenőrzése; nem lehet kisebb 1-nél és nagyobb 9-nél. A függvény azt is minősíti, ha nem történt karakterbevitel.

 

karakter()

Csakis M, D, C, L, X, V, I karakterek kerülhetnek a rendszerbe.

 

nemMegengedettString()

Nem kerülhetnek a rendszerbe nem megengedett karakterkombinációk, például IIII. Vezérlő metódusa az equals() függvény. A nemMegengedettString() függvényt részben kiválthatja a karakterPozicio2() metódus rutinja, tehát a 2 metódus (de csak részben!) hasonló funkcionalitású. Ám a biztonság kedvéért, illetve azon okból, miszerint a rémbonyolult római számszabályokat a jelen pillanatban képtelen vagyok teljes egészében átlátni-, mindkét metódus önállóan működik.

 

nemMegengedettString2()

Nem kerülhetnek a rendszerbe további, nem megengedett, de egyre speciálisabb karakterkombinációk, például IIIIVIII. Vezérlő metódusa a contains() függvény.

 

karakterPozicio()

Karakterpozíciókat minősít az alábbi szabályok alapján:

karakterPozicio2()

További, egyre speciálisabb karakterpozíciós hibák lekezelése:

Nézzük meg az implementációt, amely önmagában NEM futtatható. Önálló projektbe való illesztéséhez vissza kell lapoznunk a Váltogassunk tovább: római-arab szám váltó I. című fejezet utolsó Java-kódjához.


    static String [] nemMegengedettString = new String[58];
    static {
        nemMegengedettString[0] = "IIII";
        nemMegengedettString[1] = "IIIII";
        nemMegengedettString[2] = "IIIIII";
        nemMegengedettString[3] = "IIIIIII";
        nemMegengedettString[4] = "IIIIIIII";
        nemMegengedettString[5] = "IIIIIIIII";
        nemMegengedettString[6] = "IIV";
        nemMegengedettString[7] = "IIIV";
        nemMegengedettString[8] = "VV";
        nemMegengedettString[9] = "VVV";
        nemMegengedettString[10] = "VVVV";
        nemMegengedettString[11] = "VVVVV";
        nemMegengedettString[12] = "VVVVVV";
        nemMegengedettString[13] = "VVVVVVV";
        nemMegengedettString[14] = "VVVVVVVV";
        nemMegengedettString[15] = "VVVVVVVVV";
        nemMegengedettString[16] = "IIX";
        nemMegengedettString[17] = "IIIX";
        nemMegengedettString[18] = "XXXX";
        nemMegengedettString[19] = "XXXXX";
        nemMegengedettString[20] = "XXXXXX";
        nemMegengedettString[21] = "XXXXXXX";
        nemMegengedettString[22] = "XXXXXXXX";
        nemMegengedettString[23] = "XXXXXXXXX";
        nemMegengedettString[24] = "XXL";
        nemMegengedettString[25] = "LL";
        nemMegengedettString[26] = "LLL";
        nemMegengedettString[27] = "LLLL";
        nemMegengedettString[28] = "LLLLL";
        nemMegengedettString[29] = "LLLLLL";
        nemMegengedettString[30] = "LLLLLLL";
        nemMegengedettString[31] = "LLLLLLLL";
        nemMegengedettString[32] = "LLLLLLLLL";
        nemMegengedettString[33] = "XXC";
        nemMegengedettString[34] = "CCCC";
        nemMegengedettString[35] = "CCCCC";
        nemMegengedettString[36] = "CCCCCC";
        nemMegengedettString[37] = "CCCCCCC";
        nemMegengedettString[38] = "CCCCCCCC";
        nemMegengedettString[39] = "CCCCCCCCC";
        nemMegengedettString[40] = "CCD";
        nemMegengedettString[41] = "CCCD";
        nemMegengedettString[42] = "DD";
        nemMegengedettString[43] = "DDD";
        nemMegengedettString[44] = "DDDD";
        nemMegengedettString[45] = "DDDDD";
        nemMegengedettString[46] = "DDDDDD";
        nemMegengedettString[47] = "DDDDDDD";
        nemMegengedettString[48] = "DDDDDDDD";
        nemMegengedettString[49] = "DDDDDDDDD";
        nemMegengedettString[50] = "CCM";
        nemMegengedettString[51] = "CCCM";
        nemMegengedettString[52] = "MMMM";
        nemMegengedettString[53] = "MMMMM";
        nemMegengedettString[54] = "MMMMMM";
        nemMegengedettString[55] = "MMMMMMM";
        nemMegengedettString[56] = "MMMMMMMM";
        nemMegengedettString[57] = "MMMMMMMMM";
    }

static boolean romaiSzamValidator(String romaiString){
    rosszAdat = false;
    rosszAdat = hossz(romaiString);
    rosszAdat = karakter(romaiString);
    rosszAdat = nemMegengedettString(romaiString);
    rosszAdat = nemMegengedettString2(romaiString);
    rosszAdat = karakterPozicio(romaiString);
    rosszAdat = karakterPozicio2(romaiString);
    return rosszAdat;
}

//Hiba: például nincs bemeneti String vagy VVVVVVVVVV (10 karakter)
static boolean hossz(String romaiString){
    boolean rosszAdat = false;
    if((romaiString.length() < 1 || romaiString.length() > 9)){
        rosszAdat = true;
    }
    return rosszAdat;
}

//Hiba: például a
static boolean karakter(String romaiString){
    for(int i = 0; i < romaiString.length(); i++){
    karakter = romaiString.charAt(i);
    if(karakter != 'I'
        && karakter != 'V'
        && karakter != 'X'
        && karakter != 'L'
        && karakter != 'C'
        && karakter != 'D'
        && karakter != 'M'){
        rosszAdat = true;
        }
    }
    return rosszAdat;
}

//Hiba: például IIIIIIIII
static boolean nemMegengedettString(String romaiString){
    for(int i = 0; i < nemMegengedettString.length; i++){
    if(nemMegengedettString[i].equals(romaiString)){
        rosszAdat = true;
    }
    else
        continue;
    }
    return rosszAdat;
}

//Hiba: például IIIVVIII
static boolean nemMegengedettString2(String romaiString){
    for(int i = 0; i < romaiString.length(); i++){
        for(int j = 0; j < nemMegengedettString.length; j++){
        if(romaiString.length() >= nemMegengedettString[j].length()){
            if(romaiString.contains(nemMegengedettString[j])){
                rosszAdat = true;
            }
        }
        else
            continue;
        }
    }
    return rosszAdat;
}

//Hiba:
//V nem lehet X előtt
//I, V nem lehet L előtt
//I, V, L nem lehet C előtt
//I, V, X, L nem lehet D előtt
//I, V, X, L, D nem lehet M előtt
static boolean karakterPozicio(String romaiString){
//A tombIndexOfKarakter tömb létrehozása, inicializálása
//M=0 D=1 C=2 L=3 X=4 V=5 I=6
for(int i = 0; i < tombIndexOfKarakter.length; i++){
    tombIndexOfKarakter[i] = 0;
}

for(int i = 0; i < romaiString.length(); i++){
    karakter = romaiString.charAt(i);
    if(karakter == 'M'){
        tombIndexOfKarakter[0] = (romaiString.indexOf('M')) + 1;
    }
    else if(karakter == 'D'){
        tombIndexOfKarakter[1] = (romaiString.indexOf('D')) + 1;
    }
    else if(karakter == 'C'){
        tombIndexOfKarakter[2] = (romaiString.indexOf('C')) + 1;
    }
    else if(karakter == 'L'){
        tombIndexOfKarakter[3] = (romaiString.indexOf('L')) + 1;
    }
    else if(karakter == 'X'){
        tombIndexOfKarakter[4] = (romaiString.indexOf('X')) + 1;
    }
    else if(karakter == 'V'){
        tombIndexOfKarakter[5] = (romaiString.indexOf('V')) + 1;
    }
    else if(karakter == 'I'){
        tombIndexOfKarakter[6] = (romaiString.indexOf('I')) + 1;
    }
}

if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[0]) && ((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[0]) && ((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[4] < tombIndexOfKarakter[0]) && ((tombIndexOfKarakter[4] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[3] < tombIndexOfKarakter[0]) && ((tombIndexOfKarakter[3] != 0) && (tombIndexOfKarakter[0] != 0))
|| (tombIndexOfKarakter[2] < tombIndexOfKarakter[0]) && ((tombIndexOfKarakter[2] != 0) && (tombIndexOfKarakter[0] != 0))
)){
rosszAdat = true;
}

if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[1]) && ((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[1] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[1]) && ((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[1] != 0))
|| (tombIndexOfKarakter[4] < tombIndexOfKarakter[1]) && ((tombIndexOfKarakter[4] != 0) && (tombIndexOfKarakter[1] != 0))
|| (tombIndexOfKarakter[3] < tombIndexOfKarakter[1]) && ((tombIndexOfKarakter[3] != 0) && (tombIndexOfKarakter[1] != 0))
)){
rosszAdat = true;
}

if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[2]) && ((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[2] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[2]) && ((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[2] != 0))
|| (tombIndexOfKarakter[3] < tombIndexOfKarakter[2]) && ((tombIndexOfKarakter[3] != 0) && (tombIndexOfKarakter[2] != 0))
)){
rosszAdat = true;
}

if(((tombIndexOfKarakter[6] < tombIndexOfKarakter[3]) && ((tombIndexOfKarakter[6] != 0) && (tombIndexOfKarakter[3] != 0))
|| (tombIndexOfKarakter[5] < tombIndexOfKarakter[3]) && ((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[3] != 0))
)){
rosszAdat = true;
}

if(((tombIndexOfKarakter[5] < tombIndexOfKarakter[4]) && ((tombIndexOfKarakter[5] != 0) && (tombIndexOfKarakter[4] != 0))
)){
rosszAdat = true;
}
return rosszAdat;
}

static boolean karakterPozicio2(String romaiString){
//A tombIndexOfKarakter tömb újrafelhasználása
//M=0 D=1 C=2 L=3 X=4 V=5 I=6
for(int i = 0; i < tombIndexOfKarakter.length; i++){
    tombIndexOfKarakter[i] = 0;
}

for(int i = 0; i < romaiString.length(); i++){
    karakter = romaiString.charAt(i);
    if(karakter == 'M'){
        tombIndexOfKarakter[0] += 1;
    }
    else if(karakter == 'D'){
        tombIndexOfKarakter[1] += 1;
    }
    else if(karakter == 'C'){
        tombIndexOfKarakter[2] += 1;
    }
    else if(karakter == 'L'){
        tombIndexOfKarakter[3] += 1;
    }
    else if(karakter == 'X'){
        tombIndexOfKarakter[4] += 1;
    }
    else if(karakter == 'V'){
        tombIndexOfKarakter[5] += 1;
    }
    else if(karakter == 'I'){
        tombIndexOfKarakter[6] += 1;
    }
}

//M-hiba: például MIMI, MIIM, egyetlen kivétel MMCM
if(tombIndexOfKarakter[0] == 2 && !romaiString.contains("MM")){
    rosszAdat = true;
}
else if(romaiString.equals("MMCM") ){
    rosszAdat = false;
    return rosszAdat;
}
else if(tombIndexOfKarakter[0] == 3 && !romaiString.contains("MMM") ){
    rosszAdat = true;
}

//D-hiba: például DMD
else if(tombIndexOfKarakter[1] > 1){
    rosszAdat = true;
}

//C-hiba: például CXC, CCIC, egyetlen kivétel CCXC
else if(tombIndexOfKarakter[2] == 2 && !romaiString.contains("CC")){
    rosszAdat = true;
}
else if(romaiString.equals("CCXC") ){
    rosszAdat = false;
    return rosszAdat;
}
else if(tombIndexOfKarakter[2] == 3 && !romaiString.contains("CCC")){
    rosszAdat = true;
}

//L-hiba: például LIL
else if(tombIndexOfKarakter[3] > 1){
    rosszAdat = true;
}

//X-hiba: XIXI, egyetlen kivétel XIX
else if(romaiString.equals("XIX") ){
    rosszAdat = false;
    return rosszAdat;
}
else if(tombIndexOfKarakter[4] == 2 && !romaiString.contains("XX")){
    rosszAdat = true;
}
//XXVX, egyetlen kivétel XXIX
else if(romaiString.equals("XXIX") ){
    rosszAdat = false;
    return rosszAdat;
}
else if(tombIndexOfKarakter[4] == 3 && !romaiString.contains("XXX")){
    rosszAdat = true;
}

//V-hiba: például VIV
else if(tombIndexOfKarakter[5] > 1){
    rosszAdat = true;
}

//I-hiba: például IXI, IVII
if(tombIndexOfKarakter[6] == 2 && !romaiString.contains("II")){
    rosszAdat = true;
}
else if(tombIndexOfKarakter[6] == 3 && !romaiString.contains("III")){
    rosszAdat = true;
}
    return rosszAdat;
}