Patrzyłem na jakiś kod przez osobę i zauważyłem, że wydaje się mieć wzorzec w swoich funkcjach:
<return-type> function(<params>)
{
<initialization>
do
{
<main code for function>
}
while(false);
<tidy-up & return>
}
Nie jest zły , bardziej osobliwy (rzeczywisty kod jest dość schludny i nie jest zaskakujący). To nie jest coś, co widziałem wcześniej i zastanawiałem się, czy ktoś może wymyślić jakąś logikę za tym - być może tło w innym języku?
do...while()
i od tego czasu używa tego kodu jako szablonu funkcji.Odpowiedzi:
Możesz
break
wyjśćdo{...}while(false)
.źródło
Wiele osób zwraca uwagę, że często używa się go z przerwą jako niezręcznego sposobu pisania „goto”. To prawdopodobnie prawda, jeśli jest napisane bezpośrednio w funkcji.
W makrze OTOH
do { something; } while (false)
jest wygodnym sposobem na WYMUSZENIE średnika po wywołaniu makra, absolutnie żaden inny token nie jest dozwolony.Inną możliwością jest to, że albo była tam kiedyś pętla, albo przewiduje się, że w przyszłości zostanie dodana iteracja (np. W programowaniu sterowanym testami iteracja nie była potrzebna, aby przejść testy, ale logicznie rzecz biorąc, pętla miałaby sens, gdyby funkcja musiała być nieco bardziej ogólna niż obecnie wymagana)
źródło
Przerwa jako goto jest prawdopodobnie odpowiedzią, ale przedstawię jeszcze jeden pomysł.
Może chciał mieć zmienne zdefiniowane lokalnie i użył tej konstrukcji, aby uzyskać nowy zakres.
Pamiętaj, że chociaż najnowsze C ++ pozwala na wszystko
{...}
, nie zawsze tak było.źródło
{}
dowolnym miejscu .. Jest to szczególnie przydatne wswitch-case
kodzie{...}
gdziekolwiek jest nowocześniejszy program w C ++ (pamiętaj, że pierwszy C ++, którego użyłem, został zaimplementowany z preprocesorem i nie pozwalał na to nowoczesne użycie). autor był po prostu oldschoolowy.Widziałem, że jest to użyteczny wzorzec, gdy istnieje wiele potencjalnych punktów wyjścia dla funkcji, ale ten sam kod czyszczenia jest zawsze wymagany, niezależnie od sposobu zakończenia funkcji.
Może to uczynić męczące drzewo jeśli / inaczej-jeśli o wiele łatwiejsze do odczytania, po prostu zmuszając się do łamania za każdym razem, gdy zostanie osiągnięty punkt wyjścia, a reszta logiki jest później wbudowana.
Ten wzorzec jest również przydatny w językach, które nie mają instrukcji goto. Być może właśnie tam pierwotny programista nauczył się wzoru.
źródło
Widziałem taki kod, więc możesz go używać
break
jako swegogoto
rodzaju.źródło
To jest po prostu wypaczenie,
while
aby uzyskać sematykę programugoto tidy-up
bez użycia słowa goto.Jest to zła forma, ponieważ podczas korzystania z innych pętle wewnątrz zewnętrznej stają się dwuznaczne dla czytelnika. „Czy to ma doprowadzić do wyjścia? Czy ma to na celu jedynie wyrwanie się z wewnętrznej pętli?”
while
breaks
źródło
Myślę, że wygodniej jest pisać
break
zamiast pisaćgoto end
. Nie musisz nawet wymyślać nazwy dla etykiety, która wyjaśnia zamiar: nie chcesz przeskakiwać do etykiety o określonej nazwie. Chcesz się stąd wydostać.Istnieje również szansa, że i tak będziesz potrzebować aparatu ortodontycznego. Więc to jest
do{...}while(false);
wersja:I tak musiałbyś to wyrazić, gdybyś chciał użyć
goto
:Myślę, że znaczenie pierwszej wersji jest znacznie łatwiejsze do zrozumienia. Jest też łatwiejsze do pisania, łatwiejsze do rozszerzenia, łatwiejsze do przetłumaczenia na język, który nie obsługuje
goto
itp.Najczęściej wspominanym problemem związanym z używaniem
break
jest to, że jest źle zamaskowanygoto
. Ale w rzeczywistościbreak
jest bardziej podobny doreturn
: Obie instrukcje wyskakują z bloku kodu, który jest prawie uporządkowany w porównaniu zgoto
. Niemniej jednak obie instrukcje dopuszczają wiele punktów wyjścia w bloku kodu, co czasami może być mylące. W końcu starałbym się znaleźć jak najbardziej jasne rozwiązanie, niezależnie od tego, co jest w konkretnej sytuacji.źródło
do{ ... }while(false);
w ogóle. Ale w rzeczywistości chodzi o użycie go do naśladowania jakiegośtry{ ... }finally{ ... }
.Ta sztuczka jest używana przez programistów, którzy są zbyt nieśmiali, aby używać jawnego
goto
kodu w swoim kodzie. Autor powyższego kodu chciał mieć możliwość bezpośredniego przeskoczenia do punktu „czyszczenie i powrót” ze środka kodu. Ale nie chcieli używać etykiety i jednoznaczniegoto
. Zamiast tego mogą użyćbreak
wewnątrz ciała powyższego „fałszywego” cyklu, aby osiągnąć ten sam efekt.źródło
To bardzo powszechna praktyka. W C . Staram się o tym myśleć tak, jakbyś chciał się okłamywać w sposób „nie używam
goto
”. Myśląc o tym, nie byłoby nic złego wgoto
używaniu podobnie. W rzeczywistości zmniejszyłoby to również poziom wcięć.Mimo to zauważyłem, że bardzo często te
do..while
pętle rosną. A potem dostająif
s ielse
s do środka, przez co kod w rzeczywistości nie jest zbyt czytelny, nie mówiąc już o testowaniu.Ci
do..while
są zazwyczaj przeznaczone zrobić Clean-up . Jak najbardziej, wolałbym użyć RAII i wrócić wcześniej z krótkiej funkcji. Z drugiej strony, C nie zapewnia tak wielu udogodnień, jak C ++ , co stanowido..while
jedno z najlepszych podejść do czyszczenia.źródło
Wygląda jak programista w C. W C ++ zmienne automatyczne mają destruktory, których używasz do czyszczenia, więc nie powinno być potrzeby ich porządkowania przed powrotem. W C nie miałeś tego idiomu RAII , więc jeśli masz wspólny kod czyszczący, możesz
goto
go albo użyć pętli jednorazowej, jak powyżej.Jego główną wadą w porównaniu z idiomem C ++ jest to, że nie uporządkuje, jeśli w treści zostanie zgłoszony wyjątek. C nie ma wyjątków, więc nie był to problem, ale sprawia, że jest to zły nawyk w C ++.
źródło
Kilka wyjaśnień. Pierwsza jest ogólna, druga jest specyficzna dla makr preprocesora C z parametrami:
Kontrola przepływu
Widziałem to w zwykłym kodzie C. Zasadniczo jest to bezpieczniejsza wersja goto, ponieważ możesz się z niej wyrwać, a cała pamięć zostanie poprawnie wyczyszczona.
Dlaczego coś takiego jak
goto
byłoby dobre? Cóż, jeśli masz kod, w którym prawie każda linia może zwrócić błąd, ale musisz zareagować na wszystkie z nich w ten sam sposób (np. Przekazując błąd dzwoniącemu po wyczyszczeniu), zwykle łatwiej jest uniknąćif( error ) { /* cleanup and error string generation and return here */ }
as pozwala uniknąć powielania kodu oczyszczającego.Jednak w C ++ masz wyjątki + RAII dokładnie w tym celu, więc uznałbym to za zły styl kodowania.
Sprawdzanie średników
Jeśli zapomnisz średnika po wywołaniu makra podobnego do funkcji, argumenty mogą skurczyć się w niepożądany sposób i skompilować się do prawidłowej składni. Wyobraź sobie makro
To jest przypadkowo nazwane jako
„Inne” zostanie uznane za powiązane z elementem
gDebugModeOn
, więc kiedyfoo
jestfalse
, nastąpi dokładna odwrotność tego, co było zamierzone.Zapewnienie zakresu dla zmiennych tymczasowych.
Ponieważ do / while ma nawiasy klamrowe, zmienne tymczasowe mają jasno określony zakres, przed którym nie mogą uciec.
Unikanie ostrzeżeń „prawdopodobnie niechcianych średników”
Niektóre makra są aktywowane tylko w kompilacjach do debugowania. Definiujesz je tak:
Teraz, jeśli użyjesz tego w kompilacji wydania wewnątrz warunku, kompiluje się do
Wielu kompilatorów uważa to za to samo, co
Które często jest napisane przypadkowo. Więc dostajesz ostrzeżenie. Do {} while (false) ukrywa to przed kompilatorem i jest przez niego akceptowane jako wskazanie, że naprawdę nie chcesz tutaj nic robić.
Unikanie przechwytywania linii przez warunkowe
Makro z poprzedniego przykładu:
Teraz, w kompilacji do debugowania, ponieważ zwykle dodawaliśmy również średnik, kompiluje się to dobrze. Jednak w kompilacji wydania zmienia się to nagle w:
Lub wyraźniej sformatowany
Co wcale nie jest tym, co było zamierzone. Dodanie do {...} while (false) wokół makra zamienia brakujący średnik w błąd kompilacji.
Co to oznacza dla PO?
Ogólnie rzecz biorąc, chcesz używać wyjątków w C ++ do obsługi błędów i szablonów zamiast makr. Jednak w bardzo rzadkich przypadkach, gdy nadal potrzebujesz makr (np. Podczas generowania nazw klas za pomocą wklejania tokenów) lub jesteś ograniczony do zwykłego C, jest to przydatny wzorzec.
źródło
x =1; y = 2; { int tmp = y; y = x; x = tmp; }
.#define DBG_PRINT_NUM(n) {}
.if(a) DBG_PRINT_NUM(n); else other();
to się nie skompilujesz. Tylkoif(a) {} else other();
lubif(a) ; else other();
jest poprawne, aleif(a) {}; else other();
nie jest, ponieważ to sprawia, że klauzula „if” składa się z dwóch instrukcji.Może jest
break
używany w celu przerwania wykonywania dalszego kodu w dowolnym momencie:źródło
goto
tylko dlatego, że ktoś słyszał, że jest to „złe”.Myślę, że ma to na celu użycie
break
lubcontinue
oświadczenia. Jakiś rodzaj logiki „goto”.źródło
To proste: najwyraźniej możesz wyskoczyć z fałszywej pętli w dowolnym momencie, używając
break
instrukcji. Ponadtodo
blok jest oddzielnym zakresem (który można również osiągnąć{ ... }
tylko za pomocą ).W takiej sytuacji lepszym pomysłem może być użycie RAII (obiekty automatycznie niszczą się poprawnie po zakończeniu funkcji). Inną podobną konstrukcją jest użycie
goto
- tak, wiem, że to zło , ale można go użyć do posiadania wspólnego kodu czyszczącego, takiego jak:(Na marginesie: konstrukcja do-while-false jest używana w Lua, aby znaleźć brakującą
continue
instrukcję).źródło
Wielu respondentów podało powód
do{(...)break;}while(false)
. Chciałbym uzupełnić ten obraz o kolejny przykład z życia wzięty.W poniższym kodzie musiałem ustawić moduł wyliczający
operation
na podstawie adresu wskazywanego przezdata
wskaźnik. Ponieważ przypadek przełącznika może być używany tylko na typach skalarnych, najpierw zrobiłem to nieefektywnie w ten sposóbAle ponieważ Log () i reszta kodu powtarza się dla dowolnej wybranej wartości operacji, zastanawiałem się, jak pominąć resztę porównań, gdy adres został już odkryty. I tu się
do{(...)break;}while(false)
przydaje.Można się zastanawiać, dlaczego nie mógł zrobić tego samego
break
wif
oświadczeniu, takim jak:break
oddziałuje wyłącznie z najbliższą otaczającą pętlą lub przełącznikiem, czy to typu afor
,while
czydo .. while
typu, więc niestety to nie zadziała.źródło
Ile lat miał autor?
Pytam, ponieważ pod koniec lat 80-tych natknąłem się kiedyś na kod Fortran działający w czasie rzeczywistym, który to zrobił. Okazuje się, że to naprawdę dobry sposób na symulowanie wątków w systemie operacyjnym, który ich nie ma. Po prostu umieszczasz cały program (swój program planujący) w pętli i wywołujesz swoje „procedury wątku” jeden po drugim. Same procedury wątku są pętlami, które wykonują iterację aż do wystąpienia jednego z wielu warunków (często jeden jest określoną ilością Jest to „wielozadaniowość kooperacyjna”, w której poszczególne wątki od czasu do czasu rezygnują z procesora, aby inne nie zostały zagłodzone. Możesz zagnieżdżać wywołania podprogramu zapętlonego, aby symulować priorytet wątku Zespoły.
źródło
Oprócz wspomnianych już przykładów „goto”, idiom do ... while (0) jest czasami używany w definicji makra, aby zapewnić nawiasy w definicji, a kompilator nadal pracuje z dodaniem średnika na końcu wywołanie makro.
http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/52f670f1292f30a4?tvc=2&q=while+(0)
źródło
Zgadzam się z większością plakatów o użyciu jako słabo zamaskowanej goto. Wspomniano również o makrach jako potencjalnej motywacji do pisania kodu w stylu.
Widziałem również tę konstrukcję używaną w mieszanych środowiskach C / C ++ jako wyjątek dla biednych. Polecenie „do {} while (false)” z „break” może być użyte do przeskoczenia do końca bloku kodu, gdyby w pętli wystąpiło coś, co normalnie gwarantowałoby wyjątek.
Skorzystałem również z tej konstrukcji używanej w sklepach, w których obowiązuje ideologia „pojedynczego zwrotu na funkcję”. Ponownie, jest to zamiast wyraźnego „goto” - ale motywacją jest unikanie wielu punktów powrotu, a nie „pomijanie” kodu i kontynuowanie rzeczywistego wykonywania w ramach tej funkcji.
źródło
Pracuję z Adobe InDesign SDK, a przykłady InDesign SDK mają prawie każdą funkcję napisaną w ten sposób. Wynika to z faktu, że funkcje są zwykle bardzo długie. Gdzie musisz zrobić QueryInterface (...), aby uzyskać cokolwiek z modelu obiektów aplikacji. Tak więc zwykle po każdym interfejsie QueryInterface występuje błąd, który się
if
nie udał, przerwa.źródło
Wielu już stwierdziło podobieństwo między tą konstrukcją a a
goto
i wyraziło preferencje dla goto. Być może przeszłość tej osoby obejmowała środowisko, w którym goto było surowo zabronione przez wytyczne dotyczące kodowania?źródło
Innym powodem, dla którego przychodzi mi do głowy, jest to, że ozdabia nawiasy klamrowe, podczas gdy uważam, że nowszy standard C ++ nie jest w porządku (ISO C ich nie lubi). W przeciwnym razie, aby wyciszyć analizator statyczny, taki jak kłaczki.
Nie jestem pewien, dlaczego chcesz je mieć, może zakres zmienny lub korzyść z debugera.
Zobacz Trivial Do While loop i Braces are Good from C2.
Aby wyjaśnić moją terminologię (która moim zdaniem jest zgodna ze standardowym użyciem):
Nagie szelki :
Puste szelki (NOP):
na przykład
Blok :
który stanie się
jeśli nawiasy zostaną usunięte, zmieniając w ten sposób przepływ wykonywania, a nie tylko zakres zmiennych lokalnych. Zatem nie jest „wolnostojący” lub „nagi”.
Nagie nawiasy klamrowe lub blok mogą być używane do oznaczania dowolnej sekcji kodu, która może być potencjalną funkcją (wbudowaną), którą chcesz oznaczyć, ale nie w tym czasie refaktoryzacji.
źródło
To wymyślony sposób na emulację a,
GOTO
ponieważ te dwa są praktycznie identyczne:i:
Z drugiej strony, obie te rzeczy naprawdę powinny być zrobione za pomocą
if
s:Chociaż zagnieżdżanie może stać się nieco brzydkie, jeśli po prostu zamienisz długi ciąg znaków forward
GOTO
w zagnieżdżoneif
s. Prawdziwą odpowiedzią jest jednak właściwa refaktoryzacja, a nie naśladowanie archaicznych konstrukcji językowych.Jeśli desperacko próbowałeś transliterować algorytm zawierający
GOTO
s, prawdopodobnie mógłbyś to zrobić za pomocą tego idiomu. Z pewnością jest to niestandardowe i dobre wskazanie, że nie trzymasz się ściśle oczekiwanych idiomów języka.Nie znam żadnego języka podobnego do języka C, w którym funkcja do / while jest idiomatycznym rozwiązaniem na wszystko.
Prawdopodobnie mógłbyś przekształcić cały bałagan w coś bardziej sensownego, aby uczynić go bardziej idiomatycznym i znacznie bardziej czytelnym.
źródło
do..while(false)
służy do uruchamiania „undefined” razy, lecz do jednorazowego uruchamiania .continue
na końcu znajduje się instrukcja warunkowa, tak jak pokazałem. Byundefined
po prostu znaczy, że nie wiem, czy to działa więcej niż jeden raz, chyba że można przewidzieć, czy te warunki zostaną spełnione w określonym iteracji. Bez żadnychcontinue
znaków,do..while(false)
uruchomi się raz, ale także bez żadnychbreak
,while(true)
będzie działać w nieskończoność, więc „zachowanie domyślne” nie jest tak naprawdę istotne dla zrozumienia, co można zrobić z pętlami.continue
.continue
NIE powtarza pętli. „continue
Instrukcja powinna wystąpić tylko w instrukcji iteracji i powoduje przekazanie kontroli do części z kontynuacją pętli najmniejszej otaczającej instrukcji iteracji , to znaczy na koniec pętli”.Niektórzy programiści wolą mieć tylko jedno wyjście / powrót ze swoich funkcji. Użycie atrapy do {....} while (false); pozwala na „wyrwanie się” z atrapy pętli po zakończeniu i nadal mieć pojedynczy powrót.
Jestem programistą java, więc mój przykład byłby podobny
źródło
W takich przypadkach używam
źródło
To jest zabawne. Prawdopodobnie w pętli są przerwy, jak powiedzieli inni. Zrobiłbym to w ten sposób:
źródło
do..while(false)
zawsze wychodzi,while(true)
jest bardziej ryzykowna.while(true)
to poprawny idiom w większości języków. Często można go znaleźć w aplikacjach GUI jako główną pętlę programu. Ponieważ zasadniczo zakładasz, że program nie powinien umrzeć, dopóki nie zostanie mu to nakazane,do..while(false)
spowodowałoby to wszelkiego rodzaju wymyśloną logikę. Takie podejście może być bardziej ryzykowne w przypadku perfekcjonistycznego POV, ale jest semantycznie łatwiejsze, a tym samym mniej podatne na błędy dla ludzkich programistów (przepraszam, Skynet).do{...}while(false)
jest dokładnie taki sam jakwhile(true){ .. break;}
continue
, nie są takie same.