Morseovka řešení
Řešení by se mělo skládat z následujících kroků:
- vytvoření základu třídy Prekladatel
- vytvoření testů
- 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();
}
});
}
}