[C++] Program wypisujący wyrazy parzyste i nieparzyste

Kod powinien być przede wszystkim elastyczny i podatny na zmiany. Kod przejrzysty i łatwy do zrozumienia taki właśnie jest :slight_smile: A to, że ma działać, to chyba oczywiste…

Według mnie kod łatwy do zrozumienia, to taki w którym od razu widać co do czego służy i co z czego wynika, od razu widać co i w jakiej kolejności dzieje się w programie, nie ma w nim niepotrzebnych instrukcji, jednym słowem - można go “ogarnąć” spoglądając na niego. A przejrzysty, to taki który komentuje się sam, w którym nazwy zmiennych mówią, do czego zmienne służą (czyli zazwyczaj mają więcej niż 1-2 znaki). Taki, w którym w jednej linijce nie ma dwudziestu instrukcji, czy wyrażeń.

Masz rację. Zasugerowałem się tym, czego chciał autor (a przynajmniej co zrozumiałem z jego pierwszego posta) - czyli programu, który podzieli tekst zaczynający się literą, w którym wyrazy porozdzielane są dokładnie jedną spacją. Taki kod napisałem… I działał. A w końcu działający miał być przede wszystkim, czyli to dobry kod, prawda? :slight_smile: Ale skoro pojawia się konieczność, aby kod działał bez względu na liczbę spacji, to zawsze można rozbudować łatwy do zrozumienia i przejrzysty kod o kilka instrukcji, np. tak:

void strcpy(char zrodlo[], char parzyste[], char nieparz[])

{

    int iz = 0, in = 0, ip = 0; //iteratory poszczególnych tablic

    bool czyParzysty = false, bylaLitera = false; //pierwszy wyraz jest nieparzysty i nie wiemy, czy zrodlo zaczyna się literą

    char poprzedni = 32; //zakładamy, że na indeksie -1 źródła jest spacja, bo jest to potrzebne do pierwszej iteracji


    while(zrodlo[iz]) //dopóki nie natrafiliśmy na koniec łańcucha

    {

        //jeśli mamy do czynienia z literą albo spacją po wyrazie, to będziemy ją kopiować

        if(zrodlo[iz] != 32 || (zrodlo[iz] == 32 && poprzedni != 32))

        {

            if(czyParzysty)                     

                parzyste[ip++] = zrodlo[iz];    

            else                               

                nieparz[in++] = zrodlo[iz];    

            bylaLitera = true; //zapamiętujemy, że trafiliśmy na literę

        }

        //zmiana parzystości wyrazu, o ile w łańcuchu były do tej pory jakieś litery

        if(zrodlo[iz] == 32 && zrodlo[iz + 1] != 32 && bylaLitera)

            czyParzysty = !czyParzysty;

        poprzedni = zrodlo[iz];

        iz++;

    };

    parzyste[ip] = 0; //tu koniec parzystych

    nieparz[in] = 0; //a tu nieparzystych (żeby nie wyświetlało śmieci)

}

Pewno nie jest doskonały i da radę go jeszcze bardziej zoptymalizować.

Właśnie - zoptymalizować. Jest jeszcze jedna cecha dobrego kodu - wydajność. Wydawało mi się, że mój będzie wolniejszy od Twojego, no ale jak się okazuje, to w większości wypadków tak nie jest. Na dodatek im więcej spacji w tekście, tym mój jest szybszy. Czyżbym miał rację z tymi instrukcjami warunkowymi?

Zdaje mi się, że udowodniłem, że mój kod wcale taki znowu chłamowaty nie jest. A jeśli nie mam racji, to chętnie poczytam jakieś argumenty i dowód, że nie mam racji. Chętnie się czegoś nowego dowiem i nauczę.

P.S. Był tu już kiedyś taki, który tak samo, jak Ty miał wiedzę. Ale uważał przy tym, że jest najmądrzejszy i zawsze wszystko wie najlepiej, że zawsze ma rację, a nawet, że może wyrażać obiektywne osądy w subiektywnych kwestiach. Na dodatek nie trafiała do niego żadna argumentacja. Długo tu nie zagrzał, w ciągu paru miesięcy dorobił się bana, a teraz ponoć leczy kompleksy administrując forum z dwoma użytkownikami i dwoma postami.

Mam nadzieję, że nie podzielisz jego losu, bo szkoda byłoby stracić takiego eksperta.

Z poważaniem,

somekind

Dużo definicji i mało konkretów, nie da się obiektywnie ocenić, czy jakiś konkretny kod jest: przejrzysty, łatwy do zrozumienia, elastyczny. Cy dobrze rozumiem że wg ciebie lepszy jest program który jest: przejrzysty, łatwy do zrozumienia, elastyczny ale nie działający niż ten który jest: działający ale nie przejrzysty, nie łatwy do zrozumienia, nie elastyczny?

Wg mnie, program ma być:

  1. [*:363w8smy]Działający (dający poprawne wyniki, odporny na przysłowiowe blondynki, bez śmieci)

Przeczytaj uważnie, nic nie było powiedziano na temat - “dokładnie jednej spacji” - kody które powstali po “zasugerowaniu się” najprostszym przykładem zazwyczaj nie maja odporności na przysłowiowe blondynki, wg mnie takie kody nie spełniają punktu 1.

Teraz kiedy twój kod został poprawiony tak aby prawie spełnić kryteria poprawnego działania to zobacz jak skacze po twojej gmatwaninie. Pewnie spytasz dla czego nie uznaję twojego “przejrzystego”, “łatwego do zrozumienia” i “elastycznego” kodu za poprawnie działający:

const char why[]="test";

strcpy(why,p,n); // wspaniały kod tego nie przewidział bo autor się "zasugerował" - nie chce się kompilować

strcpy("Ala ma kota",p,n);

wynik:

n="Ala kota"

p="ma "

// czemu parzyste kończą się spacją a nieparzyste nie?

Zauważ że mój “chłamowaty” kod nie ma żadnych problemów. Pomów my teraz o elastyczności “przejrzystego” i “łatwego do zrozumienia” twojego “wspaniałego” dzieła:

  1. [*:363w8smy]Czy mógłbyś przerobić ten swój kod tak aby słowa w napisach wynikowych zostali oddzielone przecinkiem zamiast spacji, powiedz ile musisz do niego dodać aby to uczynić? W moim “chłamowatym” i “nie elastycznym kodzie”, wystarczy zamienić jeden znak. [*:363w8smy]A oddzielenie trzema znakama cudzysłów+spacja+cudzysłów ile trzeba dodać? W moim “chłamowatym” i “nie elastycznym kodzie”, wystarczy zamienić jeden znak i dodać dwie instrukcji bliźniaczo podobne do tej gdzie się ten znak zmieniło.

Mądre słowa, więc pomów my o wydajności :smiley: Jest chyba oczywiście że nie miałeś racji:

Pomiar czasu (moze troche potrwac ok 4 min) - nacisnij 

Proba 1 funkcja "by somekind" czas 24 sek

Proba 1 funkcja "by alex (prim)" czas 20 sek

Proba 1 funkcja "by alex (norm)" czas 17 sek

Proba 1 funkcja "by alex (adv)" czas 17 sek

Proba 2 funkcja "by somekind" czas 22 sek

Proba 2 funkcja "by alex (prim)" czas 19 sek

Proba 2 funkcja "by alex (norm)" czas 17 sek

Proba 2 funkcja "by alex (adv)" czas 17 sek

Proba 3 funkcja "by somekind" czas 22 sek

Proba 3 funkcja "by alex (prim)" czas 19 sek

Proba 3 funkcja "by alex (norm)" czas 18 sek

Proba 3 funkcja "by alex (adv)" czas 17 sek


funkcja "by somekind" min= 22; max= 24; avg= 22.67;

funkcja "by alex (prim)" min= 19; max= 20; avg= 19.33;

funkcja "by alex (norm)" min= 17; max= 18; avg= 17.33;

funkcja "by alex (adv)" min= 17; max= 17; avg= 17.00;

wyprodukowano kodem:

#include 

#include 

using namespace std;


//... tu wszystkie 4 warianta, o nazwach strcpy0 - wersja od somekind oraz strcpy1,strcpy2,strcpy3


struct { void (*strcpy)(const char *z,char *p,char *n); unsigned min,max,sum; const char *name; } Stat[]=

  {

    {strcpy0,0,0,0,"by somekind"},

    {strcpy1,0,0,0,"by alex (prim)"},

    {strcpy2,0,0,0,"by alex (norm)"},

    {strcpy3,0,0,0,"by alex (adv)"},

  };

const int StatSize=sizeof(Stat)/sizeof(*Stat);


const char test[]=

  "wyr czy jesteśmy w słowie false pomiędzy słowami par czy parzysty\

  fpfn czy trzeba dodać spacje do nieparzystych/parzystych odpowiednio\

  fpfn jeżeli true to przed kolejnym słowem dopisz spacje\

  f wskaźnik na fp fn będzie zależało od zmiennej par ";


char p[1024],n[1024];

const int StepCount=5000000;

const int TestCount=3;


int main()

  {

   cout"Pomiar czasu (moze troche potrwac ok 4 min) - nacisnij ";

   cin.get();

   for(int t=0;t
     {

      for(int s=0;s
        {

         void (*strcpy)(const char *z,char *p,char *n)=Stat[s].strcpy;

         cout"Proba "(t+1)" funkcja \""
         //cout;

         unsigned tm=time(0);

         for(int i=0;i
           {

            memset(p,0,sizeof(p));

            memset(n,0,sizeof(n));

            strcpy(test,p,n);

           }

         tm=time(0)-tm;

         if(t)

           {

            Stat[s].sum+=tm;

            if(Stat[s].mintm) Stat[s].min=tm;

            if(Stat[s].max
           }

         else

           {

            Stat[s].min=tm;

            Stat[s].max=tm;

            Stat[s].sum=tm;

           }

         cout
        }

     }

   cout
   for(int s=0;s
     {

      cout.setf(ios::fixed);

      cout

        

         "funkcja \""
         setw(32-strlen(Stat[s].name))" "

         "min="
         "; max="
         "; avg="
         endl

        ;

     }

   cin.get();

   return 0;

  }

Jak poprawisz swój kod aby nie bylo zbędnych spacji końcowych to różnica będzie jeszcze bardziej drastyczna.

Owszem, o ile wciskanie kitu nazywa się dowodami.

Masz zadziwiające zdolności “lania wody” bez żadnych konkretów. Kto? Jakie argumenty?

Wygłąda na to że ty od niego sporo się nauczyłeś - “wyrażać obiektywne osądy w subiektywnych kwestiach”, ponieważ kwiestii “przejrzystości” oraz “łatwości do zrozumienia” są jak najbardziej subiektywne.

Z poważaniem,

[alex]

Elastyczny, czyli łatwy w rozbudowie - to można ocenić próbując go rozbudować i stwierdzając, ile modyfikacji należało wprowadzić, aby to zrobić.

Czy jest przejrzysty i łatwy do zrozumienia można sprawdzić empirycznie, np. pytając ileś osób, czy subiektywnie taki dla nich jest. Ale należy się też trzymać pewnych zasad - takich jak np. czytelne nazewnictwo, odpowiednie formatowanie kodu, używanie białych znaków. Niestety nie ja wymyśliłem te zasady, zrobili to ludzie o wiele ode mnie mądrzejsi. O tym jest mowa w każdej książce do nauki programowania, na każdym kursie na studiach, pewno na zajęciach w szkołach ponadgimnazjalnych także. Z tego co widzę, Twój kod się tych podstawowych zasad nie trzyma, przynajmniej moim zdaniem.

Ja tak nie napisałem.

Mam wrażenie że ty zawszę najpierw coś zrobisz a potem dopiero, ewentualnie myślisz :lol:

Mam napisać czemu to co podałeś jest złe czy sam się domyślisz?


Proszę zastanawiać się nad wydawaniem takich opinii

Asterisk

Chyba nie doczytałeś, co napisałem nt. różnych rodzajów programowania - cóż, powtarzał się nie będę.

Jak widzę, nie chcesz dojść do żadnego konsensusu czy wniosków, będziesz się czepiać.

Owszem, w tym wypadku istnieje możliwość odwołania się do komórek pamięci spoza tablicy, ale rozwiązanie tego problemu jest tak banalne, że myślę, że dasz sobie radę z poprawieniem tego. I dodanie sprawdzania jeszcze dwóch warunków w dwóch instrukcjach warunkowych będzie miało znikomy wpływ na wydajność, bo pewno zaraz się do tego doczepisz.

W tym wątku z mojej strony EOT.

Doczytałem, kondensując do jednego zdania:

Istnieje dwa rodzaje programowania twoje doskonałe pod każdym względem i inne chłamowate.

Właśnie chcę dojść do jakiś wniosków i wkrótce dojdziemy :lol: dzięki tej oto ankiecie.


W związku z naruszeniem regulaminu, skutkujacym przyznaniem

warna oraz próbą wywołania zadymy na forum - linkowany

temat wylądował w Śmietniku.

Asterisk

Czekawa “zadyma”, wypowiem się w tej sprawie.

[alex], pewnie słyszałeś stare porzekadło: - " Nie kłóć się z głupcem, bo ludzie nie zauważą różnicy"

Heh, hipokryta ze mnie, właśnie na tym forum dostałem bana za to że nie czerpałem z tej mądrości ludowej.

" Głupi uczy się na błędach własnych, a mądry na cudzych", więc bądź tym mądrym skoro mi się nie udało. :lol:

Z tym że nie tylko ja na tym forum mam hipokryzję:

  • [*:1jkuc27s]

Każdy małołat po szkołnym kursie programowania jest w stanie zaauważyc, że to jest to samo co:

if(zrodlo[iz] != 32 || poprzedni != 32)   [/code]

Owszem dopasowałeś się, pewnie zadanie:

Wydrukuj tablice posortowaną wzrastająco np int T[4]={1,4,3,2};

rozwiążesz następująco:

[code=php]cout<<T[0]<<’,’<<T[3]<<’,’<<T[2]<<’,’<<T[1]<<endl;  

Program to nie samochód, jeżeli nakarmisz program nie odpowiednim paliwem to ma się nie zepsuć nie wydawać bzdurnych wyników tylko albo sobie z tymi danymi jednak poradzić albo wydać stosowny komunikat. Program który nie jest durnioodporny nie można uznać za poprawnie działający.

[alex], rozumiem że somekind nie chcę wnikać w nie swój kod, bo kompletnie nie ma pomysłu co autor kodu chciał przez to osiągnąć

ale po tobie tego się nie spodziewałem, widzę że nie dałeś sobie trudu przeanalizować pierwotny kod Quentin’a, pewnie zastanawiasz się skąd ja to wiem, za chwile wyjaśnię.

W tej “zadymie” jednak zostali nie rozstrzygnięte kwestii elastyczności, czytelności kodu, wydajności oraz użytych algorytmów.

  1. [*:1jkuc27s] elastyczność

:smiley: Tylko Quentin nie oberwał :smiley:

Masz rację, obejrzałem początek zauważyłem pierwszy z brzegu błąd w podejściu i walnąłem swoją wersję.

Na temat instrukcji for/while , oczywiście pamiętam, ale wygląda na to że nie wystarczająco dobrze.

Ciekawa koncepcja z tym int par; w mojej zmodyfikowanej przez ciebie wersji wskaźnikowej, jak się spotkamy to wyjaśnisz mi czemu ot tak wywaliłeś tą moją wersję z podwójnym wskaźnikiem, przecież mocno by zyskała przy takim samym podejściu jak w wersji wskaźnikowej, albo takim zastosowaniu dwóch strażników końca jak w wersji Quentina, na razie sam poeksperymentuje.

Zamiast twojego przykładu z tablica proponuję:

Program obliczający minimalny czas dojazdu.

Proszę podać odległość do punktu docelowego [km]: 700

Proszę podać parametry paliwa: dobre

Przy zastosowaniu takiego paliwa twój samochód może się poruszać z maksymalną prędkością -400 km/h

Do punktu docelowego dotrzesz nie wcześniej niż za -1 godzinę 45 minut.

:lol:

Ulepszyłem nieco ten poprawiony tobą mój tester: kod dostępny tu Teraz on sam mniej więcej ustawia ilość kroków w każdej próbie tak aby najszybsza funkcja była trochę ponad sekundę, inne idą z taką samą ilością kroków. Łatwo przerobić to narzędzie aby testowało nie koniecznie programy z tego tematu, polecam innym do podobnych testów.

No nie wiem czy mam się tak łatwo, że tak powiem nie omamiać… Jestem początkującym w C++ doszedłem na razie do wskaźników i po prostu do tej pory nie nauczyłem się pisać zoptymalizowanych programów - tzn. wymyślę kod, ale i tak czasem (tylko czasem !) da się go przedstawić prościej. Więc chyba warto dawać się omamiać na początku ekspertom, którzy wytkną mi błędy przez co w końcu, kiedyś nabiorę doświadczenia i ten problem mam nadzieję zniknie :roll:

Małe wyjaśnienie: http://sjp.pwn.pl/lista.php?co=zazwyczaj

Np. iteratorom pętli (tak jak większość znanych mi osób) nadaję takie właśnie krótkie nazwy. W tym akurat nic złego nie ma.

Pewnie nie.

Ja nadal jestem przekonany, że działanie i odporność na błędy to dwie różne sprawy. Oczywiście, że można powiedzieć, że program odporny na błędy wprowadzone przez użytkownika jest “lepszy”, ale takie twierdzenie nie musi dotyczyć każdej pojedynczej jego funkcji, bo można się przed nimi zabezpieczać w innych miejscach. W tym akurat przypadku można by założyć, że do takiej funkcji trafia już znormalizowany ciąg znaków (co może się odbywać np. w innej funkcji), spełniający wymogi np. mojej pierwszej wersji, więc zabezpieczenia byłyby zbędne. Jak już pisałem - zależy, czy programujemy w oparciu o kontrakt czy defensywnie.

Ty przyznajesz mi rację? Czy ja śnię?!

Dzięki za jasne wyjaśnienie tej kwestii, człowiek uczy się przez całe życie, a teraz już wszystko jest jasne.

Miło, że napisałeś sensownego posta i obiektywnie wyraziłeś w tej kwestii (mimo że mnie nie lubisz i ciągle nie możesz powstrzymać się od dogryzania mi :P)

Cały czas chodziło o stwierdzenie np. co u mnie jest źle - a alex tego nie był w stanie zrobić.

Natomiast nadal nie sądzę, żeby kod alexa był dobrym pod względem dydaktycznym dla początkującego, który próbuje rozwiązać ćwiczenie polegające na przekazywaniu tablic do funkcji. Chociażby ze względu na brak konwencji, komentarzy, niejasne nazwy zmiennych oraz użycie wskaźników, których miało nie być.

Nie, nigdy nie warto. Posłuchaj ekspertów, zrozum co i jak radzą a potem i tak zrób po swojemu i sobie porównaj wyniki. W tym przypadku zrezygnowałeś z dobrego pomysłu, uginając się pod sugestiami osób bardziej doświadczonych. Jest taka anegdota może niezbyt śmieszna ale pasująca do tego przypadku i pouczająca.

W jednej firmie pracownicy mieli pewien problem, jednak wszyscy w firmie wiedzieli że tego nie da się załatwić, dopóki nie pojawił się nowy pracownik i załatwił to, ponieważ nie wiedział że tego nie da się załatwić.

Osoby obciążone doświadczeniem zachowują się właśnie jak starzy pracownicy tej firmy.

Małe wyjaśnienie: większość, 1/4 w twoim pierwotnym kodzie i 3/6 w tym poprawionym chyba nie można nazwać większością.

[alex] zauważył, ale ten atut (jak on się wyraził) trzymał na dobitkę.

Pewnie nie ? Czyli możliwe że tak? :lol:

Kiedyś trafiła do mnie funkcja napisana przez kogoś

int GetFilePath(const char *File,char *Path,int PathSize); [/code]

tylko że po jej wywołaniu w napisie Path czasami była na końcu łamana a czasami nie (skojarzysz ze spacją na końcu wyników twojego kodu). Powiedz mi czy nazwał byś tą funkcję działającą? [alex] w tym aspekcie ma racje, do każdego zadania trzeba zabierać się kompleksowo.

Przecież chodziło o cele dydaktyczne, im więcej widać na pierwszy rzut oka tym lepiej. Kiedy się pracuje w grupie programistów to zazwyczaj cała grupa (lub manager) ustała zasady których trzeba się trzymać niezależnie od tego jak to się robi zazwyczaj. Natomiast kiedy piszę sam na zamówienie to właśnie staram się używać jak najkrótszych nazw. Np:

[code=php]struct Punkt { double wspolrzedna_x,wspolrzedna_y; } 

chyba się zgodzisz że wygląda idiotycznie?

Do ciebie osobiście nic nie mam, a nie lubię twojego podejścia do dyskusji, napadasz zamiast konstruktywnie wyjaśniać lub zapytać kiedy nie rozumiesz, czepiasz się drobnych szczegółów a pomijasz kwestie ważne jak by ich wcale nie było.

Co do braku konwencji wg mnie mocno przesadzasz. Komentarzy na ile widzę są. Co do wskaźników to po pierwsze podał 3 rozwiązania jedno z których było bez wskaźników, po drugie ty też podałeś wersje ze wskaźnikami. Co do nazw zmiennych, to czemu wprost nie powiedzieć o tym zamiast napadać:

Z resztą jak się okazało to właśnie twoje rozwiązania byli bardziej prymitywne i przykładami jak nie należy programować (bo brak elastyczności, problem rozwiązany nie kompleksowo). Jak nie zrozumiałeś poco [alex] tak “skomplikował” to chyba wystarczyło by zapytać, gdyby tak uczyniłeś to założę się nie było by tej zadymy. I może wspólnie zwrócili by uwagę na to co na początku próbował zrobić Quentin. Jeżeli czegoś nie rozumiesz to nie oznacza że jesteś głupi, człowiek mądry nie boi się przyznać że czegoś nie zrozumiał bo mu zaraz wyjaśnią i będzie już rozumieć, natomiast głupol boi się przyznać, bo zaraz zaczną wyjaśniać a on znowu nie zrozumie, więc będą próbować nadał dopóki nie wyjdzie na jaw że jest głupolem.

Wszystkie iteratory nazwałem “krótko”, a pozostałe zmienne “długo” - tak jak robię zazwyczaj. Bo dzięki temu zerkając na nazwę zmiennych od razu wiadomo do czego są, a krótkie nazwy iteratorów przy odwołaniach do elementów tablic i pętlach nie zaciemniają kodu. Zresztą - zastosowane przeze mnie nazwy iteratorów, mimo że krótkie, też są jasne - i jak iterator, n jak nieparzyste, p jak parzyste, z jak zrodlo.

To akurat bardzo idiotycznie :wink: Bo powszechnie wiadomo, że punkty opisuje się przy użyciu współrzędnych, to wynika niejako z dziedziny problemu.

Ale jak się pisze czy samemu, czy nawet tylko dla siebie, to i tak warto pisać jasno i przejrzyście, komentując, żeby wiedzieć o co chodziło, gdy za pół roku się zajrzy do kodu znowu - przynajmniej ja wychodzę z takiego założenia.

Widocznie Ty lepiej tłumaczysz od niego - bo napisałeś merytorycznie co jest nie tak i dlaczego, a nie gdybałeś i kręciłeś, jak on.

Ok, przyznaję, że za bardzo naskoczyłem na jego kod, który co prawda jest lepszy niż moja propozycja, ale był wtedy podany w niejadalnej formie. Dwie z trzech wersji były nie na temat, żadna nie była w ogóle skomentowana, nazwy zmiennych bez mała losowe - to nie był przykład dydaktyczny. Gdyby od razu wyglądał tak, jak jest teraz (bo jak widzę został edytowany) - pewno w ogóle bym się nie odezwał.

A z tego, co widzę, to zadyma się zrobiła po tym, jak alex za bardzo wkurzył się na moją uwagę o chłamowatości i przestał przestrzegać niektórych reguł.

Witam, nie czytałem całego tematu, ale widzę nieźle tu rozprawiacie i rzucacie błotem. =D>

Dla porównania, mój sposób rozwiązania identycznego problemu:

podzial-stringu-wyrazy-parzyste-nieparzyste-t343044.html