4it101»Pavlickova Reseni 4

Pavlickova Reseni 4

Morseovka řešení Řešení by se mělo skládat z následujících kroků:

  1. vytvoření základu třídy Prekladatel
  2. vytvoření testů
  3. naprogramování metody preloz

Testy

testy jsou jednoduché, testovací metoda by mohla vypadat takto:

    public void testPreloz() {
        assertEquals(".- / .... / --- / .---", Prekladatel.preloz("ahoj"));
        assertEquals(".... / . / --.. / -.- / -.-- // -.. / . / -.", Prekladatel.preloz("hezky den"));
        assertEquals(".-. / ..- / -.-. / .... / ", Prekladatel.preloz("ruch"));
    }

Metoda preloz, varianta 1

První varianta je nejjednodušší, k řešení mám následující komentáře:

  • konstruktor je private - vytváření instancí této třídy je zbytečné či spíše problematické,
  • mám vytvořenu soukromou metodu pro překlad jednoho znaku, v metodě preloz ji volám pro jeden znak, metoda preloz vypadá takto:
    public static String preloz (String vstupniText) {
        String morse = "";
        for (int i =0; i<vstupniText.length(); i++) {
            morse = morse + prelozZnak(vstupniText.charAt(i));
        }
        return morse;
    }
  • v metodě pro překlad jednoho znaku zjišťuji oddělovač slov (obvykle mezeru) pomocí metody isWhitespace třídy Character. Dále převedu znak na String, abych ho mohl porovnávat s řetězci v poli KODOVANI. Parametr soukromé metody mohl být i typu String - poté odpadne konverze z typu char na typ String. V cyklu for pro procházení pole KODOVANI po nalezeni řetězce v poli KODOVANI přeruším cyklus pomocí break. Soukromá metoda vypadá následovně:
    private static String prelozZnak(char znak) {
        String vysledek = CHYBNY_ZNAK;
        String znakU = String.valueOf(Character.toUpperCase(znak));
        if (Character.isWhitespace(znak)) {
            vysledek = ODDELOVAC_SLOV;
        }
        else {
            for (int i=0; i<KODOVANI.length; i++) {
                if (znakU.equals(KODOVANI[i][0])) {
                    vysledek=KODOVANI[i][1];
                    break;
                }
            }
        }
        return vysledek+ODDELOVAC_ZNAKU;
    }

Řešení má několik nevýhod:

  • výsledný řetězec obsahuje na konci zbytečně ODDELOVAC_ZNAKU, např. "ahoj" se přeloží do ".- / .... / --- / .--- / ",
  • mezera je "zbytečně" obklopena oddělovači znaků, např. "hezky den" se přeloží do ".... / . / --.. / -.- / -.-- / // / -.. / . / -. / ",
  • znak ch se překládá jako c a h, např. slovo "ruch" se převede do ".-. / ..- / -.-. / .... / " místo do správnějšího ".-. / ..- / ---- / ",

Metoda preloz, varianta 2

V této variantě se pokusíme odstranit nedostatky předchozí verze. Dále zde budou ještě dvě úpravy:

  • případné počáteční a koncové mezery (a další whitespace) se budou ignorovat,
  • posloupnost mezer (a dalších whitespace) se bere jako jeden oddělovač slov,

Testovací metoda vypadá následovně:

    public void testPreloz() {
        assertEquals(null, Prekladatel.preloz(null));
        assertEquals("", Prekladatel.preloz(""));
        assertEquals("", Prekladatel.preloz(" "));
        assertEquals("", Prekladatel.preloz("\t"));
        assertEquals(".- / .... / --- / .---", Prekladatel.preloz("ahoj"));
        assertEquals(".- / .... / --- / .---", Prekladatel.preloz(" ahoj "));
        assertEquals(".- / .... / --- / .---", Prekladatel.preloz(" ahoj\t"));
        assertEquals(".- / .... / --- / .--- / *", Prekladatel.preloz("ahoj+"));
        assertEquals(".... / . / --.. / -.- / -.-- // -.. / . / -.", Prekladatel.preloz("hezky den"));
        assertEquals(".-. / ..- / ----", Prekladatel.preloz("ruch"));
    }

Ve vlastním řešení je použit následující postup:

  • setřídím pole řetězců pro kódování dle délky prvního řetězce (tím se umístí písmeno CH na první místo pole),
  • pokud vstupní text je null či obsahuje pouze mezery a jiné whitespace (viz popis metody isWhitespace ve třídě Character), vracím prázdný řetězec,
  • rozdělím vstupní text na slova,
  • pomocí soukromé metody prekladSlova postupně překládám vstupní text,
  • v metodě prekladSlova v cyklu hledám v setříděném poli výskyt řetězce, který odpovídá začátku slova. Nalezený kód vložím do výsledku a odstraním odpovídající počet znaků ze začátku překládaného slova (obvykle jeden znak, pro písmeno CH dva znaky),
  • používám některé pokročilejší techniky:
    • pro spojování řetězců používám instance třídy StringBuilder,
    • využívám metod třídy Arrays pro práci s polem,
    • pro třídění používám vlastní Comparator, který je napsaný jako vnitřní třída,

Kód následuje:

import java.util.Arrays;
import java.util.Comparator;


public class Prekladatel
{
//== Datové atributy (statické i instancí)======================================
    //nejsou uvedeny statické konstanty, které jsme popsali již v zadání domácího úkolu

    // pole překladů seřazené dle délky prvního znaku
    private static String [][] KODOVANI_SORT;
//##############################################################################
//== Konstruktory a tovární metody =============================================

    /***************************************************************************
     *
     */

    private Prekladatel() {
    }

//== Nesoukromé metody (instancí i třídy) ===============================================
    public static String preloz (String vstupniText) {
        // pokud neexistuje KODOVANI_SORT, tak ho vytvořím a setřídím
        // obvykle se to dělá v konstruktoru, ale se statickými metodami nemáme konstruktor,
        if (KODOVANI_SORT == null) {
            setridPole();
        }
        // kontrola, zda není vstupem hodnota null
        if (vstupniText == null) {
            return null;    
        }
        // pokud jsou na vstupu pouze mezery (whitespace), vrátím prázdný řetězec
        if (vstupniText.matches("\\s*")) {
            return "";
        }
        // ze vstupu oddělím počáteční a koncové mezery a rozdělím na slova
        String [] slova = vstupniText.trim().split("\\s+");
        // přeložím první slovo
        StringBuilder morse = new StringBuilder(prelozSlovo(slova[0]));
        // pro další slova (pokud jsou) připojím oddělovač a překlad do morseovyk
        for (int i =1; i < slova.length; i++) {
            morse.append(ODDELOVAC_SLOV);
            morse.append(prelozSlovo(slova[i]));
        }
        return morse.toString();
    }


//== Soukromé metody (instancí i třídy) ===========================================
    private static String prelozSlovo(String slovo) {
        StringBuilder vysledek = new StringBuilder();
        // převod na velká písmena
        String slovoU = slovo.toUpperCase();
        // cyklus přes všechny písmena, nelze použít for, neboť nevím, kolik najdu
        // písmen CH
        while (slovoU.length() > 0) {
            // pokud do výsledku je již vložen nějaký znak, přidám oddělovač,
            if (vysledek.length()>0) {
                vysledek.append(ODDELOVAC_ZNAKU);
            }
            // nastavím hodnoty pro přídat, že se nenajde znak,
            String pridat=CHYBNY_ZNAK;
            int odebratZnaku=1;
            // prohledávám pole KODOVANI_SORT a hledám řetězec, který odpovídá začátku slova
            for (int i = 0; i < KODOVANI_SORT.length; i++) {
                if (slovoU.startsWith(KODOVANI_SORT[i][0])) {
                    // nalezl se znak
                    pridat = KODOVANI_SORT[i][1];
                    odebratZnaku=KODOVANI_SORT[i][0].length();
                    break;
                }
            }
            // do výsledku přidám obsah pridat
            vysledek.append(pridat);
            // ze slova odeberu první znaky - obvykle 1, při nalezení CH to budou dva znaky,
            slovoU=slovoU.substring(odebratZnaku);
        }
        return vysledek.toString();
    }

    /**
     * Metoda zkopíruje pole KODOVANI do pole KODOVANI_SORT a poté pole KODOVANI_SORT přetřídí
     * dle délky první Stringu v řádku. Využívají se metody třídy Arrays, pro třídění je
     * napsán vlastní Comparator (anonymní vnitřní třída).
     */

    private static void setridPole() {
        KODOVANI_SORT = Arrays.copyOf(KODOVANI,KODOVANI.length);
        Arrays.sort(KODOVANI_SORT, new Comparator <String []> () {
            public int compare(String [] prvni, String [] druhy) {
                return druhy[0].length() - prvni[0].length();
            }
        });
    }
}