Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Szukanie wydajniejszego rozwiązania - rozdział 3, temat "Modyfikowanie tekstu i sprawdzanie jego długości"

Ostatnio zmodyfikowano 2016-07-04 09:06
Autor Wiadomość
michal11
» 2016-06-30 11:07:35
Moim zdaniem to:
C/C++
for( size_t i = 0; i < finalny.length(); i++ ) {
    if( finalny[ i ] == '<' ) { //zamiana znakow '<' na znaki '['
        finalny[ i ] = '[';
    }
    else if( finalny[ i ] == '>' ) { // jak wyzej
        finalny[ i ] = ']';
    }
}

nawet jeżeli jest krótsze to jest mniej czytelne od tego:
C/C++
for( size_t i = 0; i < finalny.length(); i++ )
{
    if( finalny[ i ] == '<' ) //zamiana znakow '<' na znaki '['
    {
        finalny[ i ] = '[';
    }
    else if( finalny[ i ] == '>' ) // jak wyzej
    {
        finalny[ i ] = ']';
    }
}

ew. przy tak krótkich ifach można jeszcze pomyśleć nad czymś takim:
C/C++
for( size_t i = 0; i < finalny.length(); i++ )
{
    if( finalny[ i ] == '<' ) { finalny[ i ] = '['; }
    else if( finalny[ i ] == '>' ) { finalny[ i ] = ']'; }
}
P-149523
karambaHZP
» 2016-06-30 14:19:49
Czepiając się szczegółów, kod nie ma zachowanej zasady SRP.
Funkcja konwertująca ma więcej niż jedną odpowiedzialność.

Funkcja konwertująca jest narzucona przez zadanie, ale można
dopisać kolejne (i wywołać je w funkcji konwertującej):
- zajmującą się podmianą znaków (argumentami są napis, znak szukany, nowy znak),
- zajmującą sie usuwaniem znaków (argumentami są napis, usuwany znak).
Dzięki temu zabiegowi mamy uniwersalne funkcje, które działają dla
różnych znaków.
P-149533
Elaine
» 2016-07-01 05:42:13
Ale wtedy z kolei kod narusza zasadę YAGNI.
P-149560
karambaHZP
» 2016-07-01 10:51:52
Ale wtedy z kolei kod narusza zasadę YAGNI.
Może jest to pisanie na wyrost, ale kod zyska na czytelności (jak zastosowanie algorytmów - michal11).
Przy sensownych nazwach, nie trzeba analizować całej funkcji, aby zrozumieć czym się zajmuje.
Rozbijanie kodu na pojedyncze funkcjonalności uważam za dobrą praktykę.

Co do przepychania się na zasady, nie ma kodu idealnego.
Przed napisaniem każdej linii warto zastanowić się, czy będę rozumiał ten kod,
gdy zajrzę do niego za kilka miesięcy.

C/C++
void usunSasiadujaceDuplikaty( string & napis, char szukanyDuplikat )
{
    size_t pos = 0;
   
    // usuń spacje z początku zdania, opcjonalny zabieg dotyczący spacji
    if( szukanyDuplikat == ' ' ) {
        while( napis[ 0 ] == szukanyDuplikat ) {
            napis.erase( 0, 1 );
        }
    }
   
    // usuń duplikaty
    while(( pos = napis.find( ' ', pos ) ) &&( pos != string::npos ) ) {
        if( pos < napis.size() - 1 ) {
            if( napis[ pos + 1 ] == ' ' ) {
                napis.erase( pos, 1 );
            }
            else {
                ++pos;
            }
        }
    }
}

void podmienZnaki( string & napis, char znakDoPodmiany, char nowyZnak )
{
    for( char & element: napis ) {
        if( element == znakDoPodmiany )
             element = nowyZnak;
       
    }
}

string konwertuj( string & napis ) // narzucone przez treść zadania
{
    string rezultat { napis };
    char szukanyDuplikat { ' ' };
    char znakDoPodmiany { '<' };
    char znakDoPodmiany2 { '>' };
    char nowyZnak { '[' };
    char nowyZnak2 { ']' };
   
    usunSasiadujaceDuplikaty( rezultat, szukanyDuplikat );
    podmienZnaki( rezultat, znakDoPodmiany, nowyZnak );
    podmienZnaki( rezultat, znakDoPodmiany2, nowyZnak2 );
   
    return rezultat;
}

int main()
{
    string oryginalnyNapis { "<b>to jest </b> testowy       napis     <b>:)" };
    cout << oryginalnyNapis << '\n';
    cout << konwertuj( oryginalnyNapis ) << endl;
}
Teraz trzeba odpowiedzieć na pytania.
Czy to jest nadmierne komplikowanie kodu?
Czy zyskujemy na zrozumieniu kodu?
Czy ewentualny błąd łatwiej naprawić w jednej małej funkcji, niż szukanie w dużej.

Lub:
C/C++
string konwertuj( string & napis )
{
    // dobrą praktyką jest używanie zmiennych zamiast magicznych liczb czy znaków
    // eliminuje to także możliwą pomyłkę przy kolejnym wpisywaniu tycz samych wartości
    string rezultat { napis };
    char szukanyDuplikat { ' ' };
    char znakDoPodmiany { '<' };
    char znakDoPodmiany2 { '>' };
    char nowyZnak { '[' };
    char nowyZnak2 { ']' };
   
    // usuń spacje z początku zdania, opcjonalny zabieg dotyczący spacji
    if( szukanyDuplikat == ' ' ) {
        while( rezultat[ 0 ] == szukanyDuplikat ) {
            rezultat.erase( 0, 1 );
        }
    }
   
    // konwersja w jednej iteracji
    size_t pos = 0;
    while( pos <( rezultat.size() - 1 ) ) {
        if( rezultat[ pos ] == szukanyDuplikat && rezultat[ pos + 1 ] == szukanyDuplikat ) {
            rezultat.erase( pos, 1 );
        }
        else if( rezultat[ pos ] == znakDoPodmiany ) {
            rezultat[ pos ] = nowyZnak;
            ++pos;
        }
        else if( rezultat[ pos ] == znakDoPodmiany2 ) {
            rezultat[ pos ] = nowyZnak2;
            ++pos;
        }
        else {
            ++pos;
        }
    }
   
    return rezultat;
}
Czy zaglądając za kilka miesięcy, będę wiedział o co tu chodzi?
Czy jednak będę musiał dokładnie przeanalizować całą funkcję?
P-149564
Szkaplerny
Temat założony przez niniejszego użytkownika
» 2016-07-03 22:34:36
Dzięki za konstruktywną dyskusję i wskazanie błędów.
@michal11 - co do formatowania - takie rozwiązanie wyrobił u mnie VS, nie zmieniałem tego, i jakoś się przyzwyczaiłem, że nawet jest czytelnie, jak dla mnie ofc.
@karambaHZP - zupełnie zapomniałem o zadawaniu samemu sobie pytań, tak jak to zrobiłeś po każdym przykładzie i dzięki za ich okomentowanie.
P-149654
Elaine
» 2016-07-04 09:06:47
Większość podanych tu rozwiązań ma poważny problem: są niepotrzebnie kwadratowe. Da się to zrobić w czasie liniowym, choćby i przez std::replace i std::unique.
Gdyby koniecznie miało to być zrobione w jednej pętli, to pewnie zrobiłbym tak:
C/C++
std::string konwertuj( std::string string )
{
    std::string::size_type dest_index { 0 };
    for( std::string::size_type i { 0 }; i < string.size(); ++i ) {
        const char current_char { string[ i ] };
        if( current_char == ' ' && string[ i + 1 ] == ' ' ) {
            continue;
        }
        switch( current_char ) {
        case '[': string[ dest_index ] = '<'; break;
        case ']': string[ dest_index ] = '>'; break;
        default: string[ dest_index ] = current_char; break;
        }
        ++dest_index;
    }
    string.resize( dest_index );
    return string;
}

@michal11 - co do formatowania - takie rozwiązanie wyrobił u mnie VS, nie zmieniałem tego, i jakoś się przyzwyczaiłem, że nawet jest czytelnie, jak dla mnie ofc.
Bo jest czytelne. Każdy przecież wie, że 1TBS > Allman.
P-149660
1 « 2 »
Poprzednia strona Strona 2 z 2