1 / 6
Jan 2015

Do napisania tego tekstu zainspirowały mnie wołania o pomoc na forum. To co tutaj piszę, nie koniecznie musi być w 100% z prawdą, więc najlepiej, jak sprawdzisz to w innym, bardziej miarodajnym źródle. Jednocześnie z góry przepraszam, że sam tego nie zrobiłem, ale wtedy, musiałbyś jeszcze długo czekać, lub ten tekst nigdy by nie powstał. Jednocześnie, chciałbym zaznaczyć, że ja też jeszcze niedawno miałem problemy i też pisałem z błędami podobnymi do tych błędów, popełnianych przez wołających.
Dlatego, faktu że cytuję błędny kod proszącego o pomoc uzera, proszę nie odbierać personalnie,:
[bbone=c,2310] scanf("%c",&kierunek);
if((sytuacja_x!=0)||(sytuacja_y!=0)) {
if((kierunek=='N')&&(sytuacja_y>0)) {
sytuacja_y--;
}
else if((kierunek=='S')&&(sytuacja_y<0)) {
sytuacja_y++;
}
else if((kierunek=='E')&&(sytuacja_x>0)) {
sytuacja_x--;
}
else if(sytuacja_x<0) {
sytuacja_x++;
}
}
if((sytuacja_x==0)&&(sytuacja_y==0)&&(doplynal==false)) {
printf("%d\n",i+1);
doplynal=true;
}[/bbone]
Jest w zasadzie tylko jeden mały błąd logiczny, ale może, gdy kod został napisany z pomocą switch, nie byłoby problemu? Takim błędem, jest nie przewidzenie wszystkich możliwych sytuacji i zostawienie "domyślnej" ostatniej opcji else if, z domyślnym kierunkiem 'W', a może być, np =='N' ale niezgodna z potrzebną sytuacją. Jeżeli nie jesteśmy pewni, lepiej nie korzystać z opcji default - jeżeli wszystkie inne przypadki wydaje się nam, że uwzględniliśmy, bo może jednak nie uwzględniliśmy, Czyli ostatnia opcja powinna [mogłaby] wyglądać tak:
[bbone=c,2311]else if (kierunek == 'W' && sytuacja_x < 0) ++sytuacja_x;
[/bbone]
Przy okazji pozbyłem się zbędnych nadmiarowych ozdobników, alee oczywiście, nie spowoduje to nagłego przyśpieszenia i z powodu "słabo" napisanego pozostałego kodu będzie tle.
Jakie są zalety stosowania if else lub swich oprócz względów czysto estetycznych, które są zupełnie subiektywne.
Czasami dobrze napisane if else jest szybsze od swich(){ case ' ':
W if else, programista decyduje, o kolejności przetwarzania warunków, w swich to kompilator ją ustala po swojemu - więc defalt równie dobrze można umieścić na początku.
W if else łatwiej operować zakresami.
Przykład, określenie [podział na grupy] wczytywanych znaków ASCI [warto wcześniej poznać tabelę kodów]:
[bbone=cpp,2316]
char c;
while (cin >> c){
if (c < ' ') cout << "kod sterujący\n";
if else (c < '0') cout << "kod z grupy: !\"#$%&'()*+,-./ \n";
if else (c <= '9') cout << "cyfra \n";
if else (c < 'A') cout << "kod z grupy: :;<=>?@ \n";
if else (c <= 'Z') cout << "duza litera \n";
if else (c < 'a') cout << "kod z grupy: [\\]^_` \n";
if else (c <= 'z') cout << "mala litera \n";
if else (c <= '~') cout << "kod z grupy: {|}~ \n";
else cout << "I don't know ;-( Go to the SPOJ/froum and ask! wink \n";
[/bbone]
To samo z pomocą switch, dla kilku cyfr:
[bbone=c,2317]switch(c){
default: // nie musi byc na koncu - ale to lepiej sprawdz
cout << "I don't know ;-( Go to the SPOJ/froum and ask! wink \n";
break;
case '0':
case '9':
case '1':
case '8':
case '2':
case '7':
/// i tak dalej pozostale cyfry, kolejnosc bez znaczenia
case '5':
cout << "cyfra \n";
break;
}[/bbone]

Gdzieś, przeczytałem informację, że w nowszych kompilatorach, można używać już też zakresów w instrukcji switch :

[bbone=c,2318]switch(c){
case '0' .. '9' : // ale to tez sprawdz na swoim kompilatorz
// i czy nalezy uzyc dwie czy moze trzy kropki
// a takze dla swojego, innego niz c/c++ jezyka.
}[/bbone]

  • created

    Jan '15
  • last reply

    Jan '15
  • 5

    replies

  • 1.6k

    views

  • 3

    users

  • 1

    link

Czy switch nie używa takiej optymizacji że zamiast sprawdzać warunki sekwencyjnie, traktuje argument jako wartość skoku?

swtich(arg){
  case 0: kod; break;
  case 2: kod; break;
  case 3: kod; break;
}

mogłoby być kompilowane na coś takiego

adres 0000:  jmp +(arg * 20 + 1) do przodu
adres 0001:  instrukcje kodu; jmp 0100
adres 0021:  nop; jmp 0100
adres 0041:  instrukcje kodu; jmp 0100
adres 0061:  instrukcje kodu; jmp 0100
adres 0100:  punkt wyjścia z pętli

Z tego co mi kiedyś wleciało jednym uchem, a wyleciało drugim, albo lepiej jednym okiem a drugim, [albo i jedno idrugie + skleroza] to swich jest kompilowany do tablicy skoków i dlatego kolejność case nie jest istotna. Jak jest naprawdę, to właściwie może w tym momencie nie jest ważne, Jeżeli będę chciał sprawdzić, jaka wersja jest szybsza, to zamienię w programie ify na swich, bez zmiany pozostałych elementów i porównam czasy wykonania na moich testach, a najlepiej na testach przygotowanych przez autora zadania , czyli na spoju.

PS
Jeszcze jedna ważna rzecz. Tak jak if else można zagniezdżać, pilnując starannie umieszczanie nawiasów w odpowiednich miejscach, tak samo można robić to ze switch'em. DO bloku dowolnego case [lub ich grupy] wstawiamy następny, zagnieżdźony blok switch-case, no i nie zapominamy o wstawianych w odpowiednich miejscach break'ach.

Nie wiem jak obecnie (ale raczej się nie zmieniło), w dawnych czasach switch był zamieniany w zależności od liczby elementów i od zakresu ich wartości -
działały tu różne algorytmy optymalizacji

1) sekwencję if-ów - dla niewielkiej liczby elementów
2) tablicę skoków - dla większej liczby elementów o zbliżonych wartościach elementów
3) sekwencję if-ów kaskadowych - dla większej liczby elementów o dużym zakresie wartości

Czyli, nie warto nad tym za głęboko się zastanawiać, ale przynajmniej takie podstawy znać i o nich wiedzieć. Stosowanie tego czy tego to przede wszystkim kwestia gustu i estetyki. Jeżeli potrafimy napisać jedną wersję czytelnie [to pewnie drugą też] to ją stosujmy i tyle;-)

W ramach "czystości" kodu, napisałem przed jakąś chwilą program, nawet z jakąś tam strukturą i na switchach, ale niestety, testy z zadania ok, a na spoju wa. Nie mam już siły ani ochoty się grzebać, ale jak by ktoś chciał, to proszę bardzo:
[bbone=cpp,2319]#include

using namespace std;

struct Point{
int x, y;
};

struct WiatrIwoda{
int policz(Point poz, Point meta, int d, string tabWiatry){
int i = 0;
for(; i < d; ++i){
if(poz.x == meta.x && poz.y == meta.y) break;
switch (tabWiatry[i]){
case 'N':
if(meta.y > poz.y) ++poz.y;
break;
case 'S':
if(meta.y < poz.y) --poz.y;
case 'W':
if(meta.x < poz.x) --poz.x;
break;
case 'E':
if(meta.x > poz.x) ++poz.x;
break;
default:
break;
}
}
if(poz.x == meta.x && poz.y == meta.y) return i;
else return -1;
}
};

int main(){
WiatrIwoda wiw;
int d,t;
Point s, m;
string dzWiatry;
cin >> t;
while(t--){
cin >> s.x >> s.y >> m.x >> m.y >> d >> dzWiatry;
d = wiw.policz(s, m, d, dzWiatry);
if (d == -1) cout << "NIE\n";
else cout << d << endl;
}
}
[/bbone]
PS
Teraz zauważyłem, jeden malutki błąd. W default: powinienem wpisać: count << " Go to forum i Help me please, please wink!!!" << endl; wink

PS2
Oczywiście, na "gołych" cin/cout'ach, przy tak dużych testach i tak wyśrubowanych limitach grozi tle, ale wolałbym tle niż wa.

PS3
Oczywiście mam już ac, więc spokojnie, spoko, spoko

PS4
Jak znajdziesz błąd, to raczej nie umieszczaj wyjaśnienia, zachowaj dla siebie, w ten sposób, nikt bezmyślnie nie będzie wklejał prawie gotowego rozwiązania

PS5
Oczywiście testowałem......
ale tylko na danych z zadania wink

Muszę Cię trochę zmartwić, twój kod był tylko kroplą, która przepełniła czar goryczy, wypełniony obserwacjami innych kodów innych użytkowników, ale chyba Cię mocno nie zmartwiłem? forum.algoliga.pl/viewtopic.php? ... d40b#p23223 <-- zobacz pkt A

Stosowanie zaawansowanych konstrukcji - klas, struktur itd raczej mogą spowolnić, niż przyśpieszyć program. Zastosowanie szybkiego i/o na pewno dodało by speedu. W twoim programie, można jeszcze trochę powygładzać i pousuwać śmieci. Nie rozumiem, czemu zostawiłeś tego swojego śmiecia?

  1. Przecież, jak już to ktoś pisał, można w skanie dodać \n: scanf("%lf%lf%lf%lf%d[color=#FF0000]\n[/color]",&pocz_x,&pocz_y,&kon_x,&kon_y,&mapy)
  2. Lub, jak zastosowałeś wczytywanie stringu do tablicy to powinieneś to zrobić po prostu tak:
    scanf("%lf%lf%lf%lf%d%s",&pocz_x,&pocz_y,&kon_x,&kon_y,&mapy,kierunek ); i wszystkie śmieci zostaną zignorowane, nawet '\n'
  3. Dalej, po kiego grzyba [to znaczy wiem skąd to wynika <- jesteś początkujący] stosujesz jakieś dziwne typy danych? Tam wszędzie wystarczy zwyły int [ze znakiem]
  4. Parę konstrukcji masz niezgrabnych - chociaż gdy je poprawisz, one dużo nie przyśpieszą
    [list=a]
    [*][bbone=c,2336]for(i=0; i if((pocz_x==kon_x)&&(pocz_y==kon_y)&&(doplynal==false)) {
    doplynal=true;
    break;}[/bbone]np tak:
    [bbone=c,2337]for(i = 0; i < mapy && !dopltynal; i++) {
    if (pocz_x == kon_x && pocz_y == kon_y){
    doplynal = true;
    break; }// teraz ten break jest tu nadmiarowo, ale ok
    [/bbone]
    .
  5. [bbone=c,2338] if((pocz_x==kon_x)&&(pocz_y==kon_y)) doplynal=true;
    if(doplynal==true) printf("%d\n",i);
    else printf("NIE\n");[/bbone] można
    [bbone=c,2339] if (pocz_x == kon_x && pocz_y == kon_y)
    printf("%d\n", i);
    else
    printf ("NIE\n");[/bbone]
[/*:m][/list:o]

Jeżeli chodzi o sam algorytm, to teoretycznie nic tu nie można ulepszyć/przyśpieszyć, chociaż z przyjemnością mogę się podzielić kilkoma pomysłami, nie wiem, czy będą i jak bardzo skuteczne, ale to może już nie dzisiaj, ok?

PS
Dzięki, za wyjaśnieniu swojego punktu widzenia w nowe zadania w pythonie.

pozdrawiam