Pętla z wyróżnionym startem

2010-07-07 13:57

Jednym z powodów, dla których nie lubię diagramów blokowych, jest ich prymitywna notacja przedstawiania instrukcji sterujących. Jedyne, czym się tam dysponuje, to if i skok. Jak przy pomocy tylko tego można w klarowny sposób przedstawić jakikolwiek nietrywialny algorytm? To trochę tak, jakby w programowaniu używać tylko pętli nieskończonych i instrukcji break.
Można to robić, oczywiście, ale konsekwencje nie są zbyt ciekawe. Jeśli kryterium opuszczenia pętli jest tak skomplikowane, że trzeba je rozbić na kilka konstrukcji ifbreak i żadna z nich “nie zasługuje” na wyciągnięcie do warunku w samym while/for, to coś tu brzydko pachnie. Nie chciałbym być w skórze tego, kto będzie musiał później takie dzieło debugować.

W bardziej rozsądnych przypadkach da się uniknąć pętli nieskończonych, nawet jeśli od razu nie widać, jak można by to zrobić. I tak odkryłem na przykład, jak można to ładnie zrobić w sytuacji podobnej do poniższej:

  1. T foo;
  2. for (;;)
  3. {
  4.     foo = SomeFunction(sth);
  5.     if (!foo.IsOK()) break;
  6.  
  7.    // (Reszta pętli, zmieniająca sth na podstawie foo)
  8. }

Nazywam to pętlą z wyróżnionym startem, bo typowa jej postać, która nie zawiera (pozornie) nieskończonego kręcenia się w kółko, wygląda tak:

  1. for (T foo = SomeFunction(sth); foo.IsOK(); foo = SomeFunction(sth))
  2.     { /* Reszta pętli, jw. */}

Cała zabawa polega na tym, że pierwsze wywołanie SomeFunction może dać niepoprawną wartość w foo. (Może to być jakieś wyszukiwanie w pojemniku i element odpowiadający kluczowy sth nie został w ogóle znaleziony). Sama treść pętli nie może być wtedy wykonana, bo zdarzy się zapewne jakieś nieszczęście. Dlatego trzeba wyodrębnić to pierwsze wywołanie, i oczywiście zadbać także o kolejne. A to daje, jak widać, dublowanie kodu. Chciałoby się to zrobić lepiej.

I można, na szczęście. Możemy za to podziękować feature‘owi C++, który często bywa krytykowany jako ułatwiający popełnianie błędów. Bowiem to dzięki temu, że instrukcja przypisania zwraca wartość przypisywaną, możemy to wszystko zapisać tak:

  1. for (T foo; (foo = SomeFunction(sth)).IsOK(); /* nic */)
  2.     { /* Reszta pętli */ }

Zdaję sobie sprawę, że dla wielu fakt ten jest równie oczywisty jak pisanie return a == b; zamiast:

  1. if (a == b) return true; else return false;

ale dla niektórych taka postać pętli for może być zupełnie zaskakująca. Pamiętajmy, że kiedyś (prawie) każdy pisał też i takie ify jak powyżej ;-)

Tags: ,
Author: Xion, posted under Programming »


One comment for post “Pętla z wyróżnionym startem”.
  1. NullPointerException:
    July 10th, 2010 o 13:29

    Szczerze, to nie widzę problemu w konstrukcji pętli nieskończonej, której przykład podałeś (z tym, że ja wolę używać while(true) {...}). Twoje rozwiązanie jest mniej czytelne. Jak najbardziej rozumiem przywiązywanie dużej wagi do designu kodu (tutaj wzorzec, tam wyodrębniony interfejs, gdzie indziej z kolei rzucamy wyjątek lub wstawiamy asercję), ale to co Ty tutaj pokazujesz to jakaś pseudo-dbałość o kod. To takie usilne wymyślanie, jak tu uniknąć nieskończonej pętli, którą z zasady uznajesz za “be”. Szczerze mówiąc zdziwiło mnie, że nigdzie nie użyłeś makra xD

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.