nieelegancki break?

0

Witam,
Dzis na uczelni profesor powiedzial mi, ze stosowanie instrukcji "break" do przerwania petli jest "nieeleganckie". Nigdy nie spotkalem sie z takim twierdzeniem, a owy profesor stawial nawet nieelegancje breaka na rowni z goto. To,ze goto jest nieladnym sposobem chyba nikomu nie trzeba tlumaczyc. Ale czemu break? Zapytany przeze mnie jak powinienem zastapic breaka kiedy chce wyjsc z petli odpowiedzial ze np. w petli for (i=0; i<100; i++) powiniem zrobic "if (costam) i=100; Nie wiem niby w czym taki zapis ma byc ladniejszy od breaka? Moim zdaniem jest nawet mniej elegancki. Chcialbym poznac wasze zdanie na ten temat. I mam prosbe... niech wypowiadaja sie tylko osoby ktore juz dosc dlugo siedza w programowaniu i czuja co jest "eleganckie", "nieeleganckie" "ladne" lub "nieladne".

Pozdrawiam,
Marcin

0

Nie jestem jakas wyrocznia czy cos, ale dla mnie okazjonalny brejk nie jest niczym zly. Natomiast ten pomysl szanownego profesora jest jakis do bani wg mnie. Tylko jedna rzecz - to jest Twoj profesor, i jest zapewne "madrzejszy" od Ciebie (w koncu wystwaia ocene) wiec i tak musisz pisac jak chce.

0

Break jest jak najbardziej prawidłową konstrukcją. Jednak w pewnych kręgach break był (myślałem, że już nie jest - widać się myliłem)/jest krytykowany, najczęściej kiedy użyty właśnie w pętli for. Dlaczego? Ponieważ niektórzy uważają - zupełnie nie wiem na jakiej podstawie - iż prawidłowym jest, aby następna instrukcja po pętli miała prawo 'zakładać', że pętla się zakończyła, gdyż jej podstawowy warunek (ten, który konstruuje pętlę) jest spełniony.

Więc przerwanie pętli wymuszają, ręcznie wywołując spełnienie warunku. Podejście to już dawno zostało odrzucone przez wielu programistów, ponieważ wpływanie na zmienne, tylko by spełnić warunek pętli, orientuje programowanie składniowo, a nie problemowo (do orientacji problemowej właśnie przecież współcześnie się dąży) i może powodować utrudnienia w budowie logiki programu.

Dlaczego GoTo jest nieeleganckie tłumaczyć nie będę - nawet na tym forum wiele słów padło :)

0

Imho elegancja wynika ze sposobu jak sie zastosowalo dane rozwiazanie.

Jesliby posluchac twojego wykladowcy to trzeba by zastosowac pare regul w dalszym kodzie petli ;] np objac go wielkim if(i==100) //{tu reszta kodu}aby po jego if(costam) i = 100; nie wykonala sie reszta petli ktora wykonac sie nie powinna. imo ten dodatkowy if podczas kazdej petli jest niepotrzebny ;]
Jako programista nie powinienes sie kierowac "elegancja" jaka czesto narzuca sie na wykladach ( w koncu profesor/doktor/magister ma racje do momentu uzyskania od niego ostatniego wpisu xD [chyba ze trafiles na swietnych wykladowcow - a tacy sie zdarzaja]) tylko tym co jest ci w danej chwili potrzebne i bardziej skuteczne. Oczywiscie jesli bedziesz kiedys pracowal w jakims projekcie to zapewne dostaniesz zbior regul i zasad jaki przyjela reszta zespolu z jakim tworzysz dany produkt i bedziesz sie musial dostosowac. Tudziez mozesz zaproponowac lepsze rozwiazanie.
Ja na twoim miejscu robilbym to co on proponuje, uzyskal zaliczenie/ egzam na 5 i zapomnial :D

Ilu programistow tyle rodzajow elegancji.

/// Szczawik [browar] [browar]
Mialem problemy z dodaniem posta :) I byles szybszy :D

0

Faktycznie, w pętli for jeśli konieczne jest przerwanie pętli, to break; jest jak najbardziej stosowne. Do tego przecież służy. Właśnie takie ręczne wymuszanie spełnienia warunku nie ma sensu. Ale w wielu przypadkach może należałoby się zastanowić nad jeszcze lepszym rozwiązaniem (z punktu widzenia osoby, która potem analizuje kod, nie mówię, że z punktu wydajności czy coś, bo w tej kwestii nie wiem jak to się zachowuje) - skonstruowaniem warunku tak, by w sytuacji, gdy chcemy przerwać pętlę, on to robił "sam". To tyle z mojej strony.

0

Jak dla mnie to ktos kto narzeka na stosowanie rzeczy typu break ma problem ze soba, to tak jakbym przekonywal kogos, ze tlo jakiejs komorki na stronie powinno miec wartosc #444 a nie #333.

Kazdy robi jak mu wygodnie, wszyscy mamy swiadomosc, ze mozemy zrobic ifa i mozemy rowniez zrobic breaka, to co zastosujemy zalezy glownie od sytuacji i naszego upodobania, jak ktos twierdzi inaczej to sry ale imo ma male doswiadczenie.

0

nie jestem programista ale dla mnie te opinie o czyms ze jest eleganckie a inne jest bee to kompletny idiotyzm, co do tego pomyslu psorka to dziwny troche <ort>poco </ort>utrudniac <ort>zeczy </ort>proste, co do goto to nie zgadzam sie z opinia ze jest nieeleganckie, ba nawet dalekie skoki nieraz są przydatne i <ort>niebez </ort>przyczyny stosuje sie je chociazby w libpng bo jezeli jestem w jakiejs rekurencji lub w kilku zagniezdzonych petlach to goto lub skoki sa idealne a nie sprawdzanie wszystkiego po kolei. Wedlug mnei kod ma dzialac i ma byc szybki a jego zapisanie to jakby nie bylo tylko ekspresja

0

Możesz się zgadzać lub nie, ale są pewne konstrukcje, których nie należy stosować - to one są uznawane za nieeleganckie. Przykład: GoTo. Jego złe użycie pozwala wyskoczyć, lub co gorsza wskoczyć do pętli. W językach wysokiego poziomu wskok do pętli na ogół kończy się brakiem inicjalizacji licznika pętli. Niby mały problem dla doświadczonego programisty, bo zadba o to. Ale jak jesteś doświadczony, to widzisz masę lepszych rozwiązań i konstrukcji, i GoTo nie używasz wcale.

Oczywiście elegancja to temat subiektywny. Pewne grona osób mogą za niestosowne uznać coś, co dla innych jest prawidłowym - tak jak ten Break. W końcu pozwala na przerwanie pętli, mimo nie spełnienia jej warunku. To może być postrzegane jako działanie niestosowne, bo wymusza inny model programowania, eliminując programowanie asertywne (tym samym defensywne).

Nie mówimy oczywiście o estetyce w sensie wyglądu zapisu. To tak na prawdę nie ma wpływu na technikę programowania i metodologię, a jedynie jej estetykę w sensie wizualnym.

Za niebezpieczne lub nieestetyczne uważane są techniki, które właśnie wymuszają metodologię. Przykładem takiego programowania jest nie używanie funkcji (w rozumieniu: procedury zwracające wartość - jak w językach Pascalowatych), a w zamian używanie samych procedur ze zwracaniem wyników przez parametry referencyjne i wskaźnikowe. Nie pozwala to na pełne programowanie o metodologii funkcyjnej (czyli takiej, gdzie ciąg działań można zagnieździć w takiej postaci: jedna funkcja zwraca wartość, która to wartość staje się parametrem innej funkcji). Jeśli język pozwala na przykład na programowanie funkcyjne, to nieestetycznym jest napisanie bibliotecznej procedury (o ile nie jest to konieczne! - to konieczne zastrzeżenie), która zwraca wartości inaczej niż funkcja. To wymusza na programiście metodologię, a przez to on musi dostosować się do języka, a nie na odwrót.

0

xD profesor nie znaczy madry

0
cepa napisał(a)

Wedlug mnei kod ma dzialac i ma byc szybki a jego zapisanie to jakby nie bylo tylko ekspresja

Problem zaczyna się, gdy ktoś przejmuje po Tobie kod i ma go zrozumieć. Nie dotyczy to szczególnego przypadku o którym mowa, ale jako ogólna zasada ma sens.

Wydaje mi się, że logika, która mogłaby stać za wychodzeniem z pętli poprzez przypisanie do zmiennej pętli, mogłaby wyglądać następująco: kod po pętli może byc pisany z założeniem że w wyniku pętli 'i' będzie równe górnej granicy (lub górnej granicy +1) i z tej wartości jakoś korzystać.

Nie ma to jednak sensu z następujących przyczyn:

w zależności od implementacji pętli w danym języku wartość może być równa górnej granicy lub górnej granicy +1, więc łatwo się pomylić

wartość tej zmiennej może ginąć natychmiast po wyjściu z pętli wskutek optymalizacji (Delphi tak robi)

niektóre języki zabraniają przypisania do zmiennej pętli - np Delphi

PS. Co znaczy xD poza kartą w moim aparacie?

0

To, ze jedne instrukcje/sposoby programowania sa bardziej eleganckie a drugie mniej nie ulega najmniejszej watpliwosci. Goto to idealny przyklad. Nie powinno sie go uzywac w jezykach wysokiego poziomu (to nie assembler!), co dla bardziej doswiadczonych porgramistow powinno byc oczywiste. Wystarczy wstawic sobie kilka takich skokow w programie i nie dosc, ze kod przestaje byc w ogole czytelny, to jeszcze debuggowanie takiego programu staje sie mocno utrudnione. <ort>po prostu </ort>to jest nieladne i nieefektywne (z pukntu widzenia programisty nie procesora). Pytanie teraz czy break jest <ort>chodz </ort>w malym stopniu obarczony ta "nieelegancja" tak jak goto? Profesor o ktorym wspomnialem stwierdzil, zebym nie programowal nigdy z break, bo kiedy program ma 10 lini to pewnie polapie sie w nim bez problemu, kiedy bedzie mial 1000 to juz prawdopodobnie bede mial klopoty, a kiedy bedzie mial 10000 i nadal bede uzywal break to z pewnoscia sie pogubie. Ten argument moge <ort>naprzyklad </ort><ort>Od razu </ort>obalic empirycznie poniewaz mialem juz okazje pisac program ktory zawieral ok. 10000 lini i nie przypominam sobie abym gubil sie w breakach, chociaz zapewne uzylem ich tam kilka(dziesiat/set). Moje pytanie na temat breaka ma bardziej charakter filozoficzny :) tzn. lubie szukac zawsze rozwiazan lepszych, ladniejszych... wazne to jest zwlaszcza kiedy sie jeszcze uczymy programowac (a ja nadal sie ucze chociaz robie to juz od ok. 10 lat) i staram sie zawsze trzymac tych zalozen. Owy profesor o ktorym wspominalem stwierdzil, ze jesli zaprojektowalem petle z uzyciem break, to znaczy, ze zaprojektowalem je zle. Zawsze jesli zaprojektuje petle dobrze, to nie wymaga ona wyjscia z uzyciem breaka. Nie zakladam Od razu zeby to byl falsz (osoba ta jesli jest na stanowisku asystenta/cwiczeniowca w mojej uczelni musi miec spore pojecie o programowaniu bo nie przyjmuja byle kogo) mysle, ze ta osoba moze miec racje. Chce miec <ort>po prostu </ort>jakies fakty/opinie potwierdzajace to, w mysl reguly nie uwierze<ort> do poki</ort> nie dotkne/nie zobacze. Moze znacie jakis przyklad problemu, zadania ktorego rozwiazanie z breakiem bedzie o wiele mniej czytelne niz rozwiazanie bez niego?

0
void clearspaces(char* s){
  if(!s)return;
  size_t i=0;
  while(s[i]==32)i++;
  if(i)strcpy(s,&s[i]);
  i=strlen(s);
  while(i)
    if(s[--i]==32)s[i]=0;
      else break;
}
void clearspaces(char* s){
  if(!s)return;
  size_t i=0;
  while(s[i]==32)i++;
  if(i)strcpy(s,&s[i]);
  i=strlen(s);
  while(i-- && s[i]==32)s[i]=0;
  // while(i && s[--i]==32)s[i]=0; // to w niektorych przypadkach powinno byc szybsze
}

porownaj predkosc i sam sobie odpowiedz

// do postu ponizej: nie/czytelnosc kodu to nie kwestia programu, tylko wiedzy czytajacego, wiec wszelkie tego typu argumenty sa z gory nietrafione

0

IMHO stosowanie metody i=100 zwieksza nieczytelnosc kodu a w tym wypadku break jest jasny. Czesto uzywam tez po petli warunki if (i==100), gdzie sprawdzam czy petla zakonczyla sie prawidlowo, np. wyszukujac wiem, ze petla zakonczona prawidlowo oznacza nieznalezienie elementu. Oszczedzam w ten sposob na jednej zmiennej i moge wykorzystac fakt, ze petli nie zakonczyl warunek glowny.

pozdrawiam
johny

0

Marcin: Ja osobiście programuję od lat 14 i nie przypominam sobie, bym kiedykolwiek używał break, a na pewno nie przypisywałem do zmiennej sterującej żadnej wartości ponad normę (Delphi na to nie pozwala, co do Pascala nie jestem pewien, ale takich metod nie stosuję) i jakoś żyję do tej pory. Może i się da tak programować :). Ale czy jest to efektywniejsze to już inna historia. Trudne do ocenienia.

Ktoś tam pisał o libpng i użyciu goto w nim: Tutaj historia jest całkiem inna. Programiści tego typu bibliotek nie przejmują się tym na ile elegancki ma być kod - ma działać maksymalnie optymalnie za wszelką cenę. A są to ludzie na tyle doświadczeni, że zdają sobie sprawę z tego kiedy coś takiego jak goto opłaca się stosować.

0

Juz kiedys byl taki temat: http://4programmers.net/Forum/viewtopic.php?id=62392

Korzystam z instrukcji break oraz continue tylko wtedy gdy to jest potrzebne. Ale nie widze sensu uzywania jej w petli for (nie przypominam sobie abym kiedys takowej skorzystal w petli for), natomiast wcale nie uwazam ze jest to nieeleganckie, tandentne, amatorskie itp. Jak ktos to ladnie ujal - jezyk programowania udostepnia mechanizmow, po to aby z nich korzystac.

0
Adam Boduch napisał(a)

Korzystam z instrukcji break oraz continue tylko wtedy gdy to jest potrzebne. Ale nie widze sensu uzywania jej w petli for

Hm. Ja jak wiecie jestem zakamieniały amator i pisuję programy mimo że się na tym nie znam. Ale jak bez break najszybciej i zgodnie z elegancją zrobić wyszukanie pierwszego wystąpienia wartości w nieposortowanej tablicy dynamicznej?

Ja robię:

for i:=Low(Tablica) to High(Tablica) do
  if Tablica[i]=SzukanaWartosc then
    begin
      ZrobCoTrzeba;
      Break;
    end;
0
pq napisał(a)

Hm. Ja jak wiecie jestem zakamieniały amator i pisuję programy mimo że się na tym nie znam. Ale jak bez break najszybciej i zgodnie z elegancją zrobić wyszukanie pierwszego wystąpienia wartości w nieposortowanej tablicy dynamicznej?

Hmmm... nie wiem czy szybsze i czy elegantsze ale:

var
  Done : Boolean;
  Counter : Integer;
begin
  Counter := 0;
  Done := False;

  repeat
     { szukamy a jezeli znajdziemy to Done := True } 
     Inc(Counter);
  until (Done = True) or (Counter = High(Tablica)); 
end.
0

Ale w Twoim kodzie High(Tablica) jest definicyjnie obliczane za każdym obrotem pętli - i tylko można próbować liczyć na optymalizator że to usunie. W for wartość graniczna pętli jest na pewno obliczona tylko na początku.

Poza tym masz dodatkową zmienną Done.
I jeszcze: jeśli znalezienie wymaga zrobienia czegoś poza ustawieniem znacznika (trzeba wykonać ZrobCoTrzeba) to musisz wartość Done sprawdzić jeszcze raz:

 until (Done = True) or (Counter = High(Tablica)); 
end.

If Done then ZrobCoTrzeba;

Może przewaga repeat/until jest wtedy gdy trzeba tylko znaleźć indeks. Wtedy for wymaga dodatkowej zmiennej, a repeat będzie miało indeks w Counter:

for i:=Low(Tablica) to High(Tablica) do
  if Tablica[i]=SzukanaWartosc then
    begin
      index:=i;
      Break;
    end;

No ale repeat też wymaga jednej dodatkowej zmiennej Done.

BTW: Twój kod nie zajrzy do ostatniej komórki, powinno być.

  until (Done = True) or (Counter > High(Tablica)); 

A w Counter będzie szukany indeks + 1, więc trzeba jeszcze odjąć 1 żeby miec wynik. Moim zdaniem zaciemnia to kod.

Ale może moja miłość do for to wynik tego, że pierwsze kroki stawiałem w Basicu ZX Spectrum, gdzie nie było innych petli ;-)

PS. Powtarzam: Co znaczy xD poza kartą w moim aparacie?
PS2: Marcin_ powienien wydrukowac ten watek i pokazać profesorowi :-)

0

Pq, ale to juz nie problem, wartosc High(Tablica) mozna zapisac do zmiennej i bedzie git. Obydwa kody wykonywane sa mniej wiecej w takim samym czasie (for moze nawet i szybciej w niektorych wypadkach), tak wiec od programisty zalezy jakiego rozwiazania uzyje. Ja tylko odpowiedzialem na pytanie ze da sie zastapic petle for :)

0

Wiem, wiem, ja nie polemizuję, tylko raczej myslę na głos. Chyba zastanawiamy się tutaj, które rozwiązanie jest bardziej elganckie. Czyli, jak ja to rozumiem, da efektywniejszy czasowo i pamięciowo kod przy zachowaniu czytelności i przejrzystości. Moim zdaniem użycie Break w mój sposób jest co najmniej równie a może bardziej efektywne i eleganckie niż inne sposoby.

0

break uzywam, bo ... czemu nie ?

pq napisał(a)

Co znaczy xD
to jakis nowy typ usmieszka xD

0

He, a ja mysle, kombinuję, czy to coś szesnastkowo, ech...

0

int a, b, c, d, e;
for (a = 0; a < 100; a++)
for (b = 0; b < 100; b++)
for (c = 0; c < 100; c++)
for (d = 0; d < 100; d++)
for (e = 0; e < 100; e++) {
rob_cos();
if (cos_jest_nie_tak()) goto WYJAZD;
}
return 0;
WYJAZD: return -1;

prosze o inne rozwiazanie tego problemu ktore nic wiecej nie robi jak skacze do ewentualnej obslugi bledu...

0

Wszędzie gdzie czytałem wyjście z wielokrotnie zagnieżdżonej petli jest podawane jako usprawiedliwony przykład użycia goto.

0
cepa napisał(a)

int a, b, c, d, e;
for (a = 0; a < 100; a++)
for (b = 0; b < 100; b++)
for (c = 0; c < 100; c++)
for (d = 0; d < 100; d++)
for (e = 0; e < 100; e++) {
rob_cos();
if (cos_jest_nie_tak()) goto WYJAZD;
}
return 0;
WYJAZD: return -1;

prosze o inne rozwiazanie tego problemu ktore nic wiecej nie robi jak skacze do ewentualnej obslugi bledu...

Kod zachowuje się podobnie, goto nie ma, ale czy rozwiązanie lepsze to ja nie wiem:

int a,b,c;
bool wrong = false;
for (a = 0; a < 100; a++)
  {
  for (b = 0; b < 100; b++)
    {
    for (c = 0; c < 100; c++)
      {
      rob_cos();
      if (jakis_blad())
        {
        wrong = true;
        break;
        }
      }
    if (wrong) break;
    inne_rzeczy();
    }
  if (wrong) break;
  inne_rzeczy();
  }
return (wrong)?-1:0;

// ja sie przyczepie: ani lepiej ani gorzej - inaczej, za cene pozornej elegancji tracisz miejsce na stosie oraz tracisz na szybkosci (dodatkowe warunki do sprawdzania). wiec sam powinienes wyważyc, co jest w danym programie potrzebne. imo w kernelu rozwiazanie cepy byloby lepsze. we wszelkich programach uytkowych pisanych przez jedna osobe bez znaczenia. w programach pisanych przez grupe ... moze tak :

int a,b,c;
int ret = 0;
for (a = 0; a < 100; a++){
  for (b = 0; b < 100; b++){
    for (c = 0; c < 100; c++){
      rob_cos();
      if (jakis_blad()){
        ret = -1;
        break;
      }
    }
    if (ret) break;
    inne_rzeczy();
  }
  if (ret) break;
  inne_rzeczy();
}
return ret;

prawie to samo, a jeszcze czytelniejsze (pomijajac zmiane stylu pisania) [mf]

0

Ale znowu powstaje problem czytelności. Dla mnie kod cepy jest bardziej naturalny, wiadomo, co robimy. Natomiast w Twoim przypadku dochodzi cała masa if'ów. A co w sytuacji, gdybyś chciał przerwać np. inną pętlę z tych zagnieżdżonych w innym miejscu (wiem, że zakręciłem :) )? Dodajesz kolejną zmienną i parę kolejnych instrukcji warunkowych? IMHO to, co napisał któryś z przedmówców jest słuszne - język dostarcza narzędzi, nam pozostaje tylko z nich korzystać, oczywiście właściwie. Goto istnieje po to, aby go używać, tak samo z Break. Nie wolno tylko przesadzić i używać tam, gdzie rzeczywiście się przydaje i nie ma lepszych rozwiązań :)

0

W pełni się z Tobą zgadzam. Cepa poprosił tylko o inny sposób przedstawienia tych pętli bez goto, to mu go zaprezentowałem.

0

Co do instrukcji break; w pętli for to ja nie przypominam sobie, żebym kiedyś tego potrzebował... Jeśli przewiduje, że w trakcie przeszukiwania np. ciągu znaków po znalezieniu właściwego będę mógł pominąć przeszukiwanie reszty to <ort>po prostu </ort>używam pętli while. Dla mnie to jest naturalne rozwiązanie takiego problemu, takie które nasuwa się na myśl w pierwszej kolejności.

Jeśli chodzi o przykład podany przez cepa to popieram użycie goto to najszybciej rozwiąże kłopot.

Z resztą to czy dany kod jest elegancki czy czytelny jest tylko sprawą umowną. Zazwyczaj w książkach dla początkujących programistów daje się takie rady co do 'eleganckiego' programowania aby nasz rozpoczynający przygodę koder nie zniechęcił się przy szukaniu błedu w programie z kilkunastoma wywołaniami goto ;)

@do autora wątku - czy for=100; czy break; to niewielka różnica. IMHO obydwa rozwiązania są tak samo czytelne i porawne. Po otrzymaniu wpisu możesz powiedzieć profesorowi, że nadgorliwość jest gorsza od faszyzmu :P

0

co goto jako wyjścia z zagnieżdżonej pętli - od kilku lat większość nowoczesnych jezyków programowania ma cos takiego jak wyjątki. pozwalają one nie tylko wyjść z pętli, ale dowolnie zagnieżdżonych wywołań funkcji (np. przy rekurencji).

0

No ale to też nie zawsze najwygodniejsze. W rekurencji - zgodzę się, ale w zagnieżdżonej pętli IMHO lepsze goto.

1 użytkowników online, w tym zalogowanych: 0, gości: 1