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 if
–break
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:
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:
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:
Zdaję sobie sprawę, że dla wielu fakt ten jest równie oczywisty jak pisanie return a == b;
zamiast:
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 if
y jak powyżej ;-)
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