Mam następujący kod:
if (this->_car.getAbsoluteAngle() <= 30 || this->_car.getAbsoluteAngle() >= 330)
this->_car.edir = Car::EDirection::RIGHT;
else if (this->_car.getAbsoluteAngle() > 30 && this->_car.getAbsoluteAngle() <= 60)
this->_car.edir = Car::EDirection::UP_RIGHT;
else if (this->_car.getAbsoluteAngle() > 60 && this->_car.getAbsoluteAngle() <= 120)
this->_car.edir = Car::EDirection::UP;
else if (this->_car.getAbsoluteAngle() > 120 && this->_car.getAbsoluteAngle() <= 150)
this->_car.edir = Car::EDirection::UP_LEFT;
else if (this->_car.getAbsoluteAngle() > 150 && this->_car.getAbsoluteAngle() <= 210)
this->_car.edir = Car::EDirection::LEFT;
else if (this->_car.getAbsoluteAngle() > 210 && this->_car.getAbsoluteAngle() <= 240)
this->_car.edir = Car::EDirection::DOWN_LEFT;
else if (this->_car.getAbsoluteAngle() > 240 && this->_car.getAbsoluteAngle() <= 300)
this->_car.edir = Car::EDirection::DOWN;
else if (this->_car.getAbsoluteAngle() > 300 && this->_car.getAbsoluteAngle() <= 330)
this->_car.edir = Car::EDirection::DOWN_RIGHT;
Chcę uniknąć if
łańcucha; to naprawdę brzydkie. Czy istnieje inny, być może bardziej przejrzysty sposób napisania tego?
c++
if-statement
Oraekia
źródło
źródło
this->_car.getAbsoluteAngle()
raz przed całą kaskadą.this
(this->
) nie jest potrzebna i tak naprawdę nie wpływa na czytelność ..>
testów; nie są potrzebne, ponieważ każdy z nich został już przetestowany (w przeciwnym kierunku) w poprzednimif
oświadczeniu.else if
.Odpowiedzi:
Tak bym to zrobił. (Jak na mój poprzedni komentarz).
źródło
q = a/b
ir = a%b
wtedyq * b + r
muszą być równea
. Tak więc w C99 legalne jest, aby reszta była ujemna. BorgLeader, możesz rozwiązać problem za pomocą(((angle % 360) + 360) % 360) / 30
.GetDirectionForAngle
proponuję jako zamiennik dla kaskady if / else, obie są O (1) ...Możesz użyć
map::lower_bound
i zapisać górną granicę każdego kąta na mapie.Przykład roboczy poniżej:
źródło
table[angle%360/30]
odpowiedzi, ponieważ jest tani i bez gałęzi. Znacznie tańsze niż pętla przeszukiwania drzewa, jeśli kompiluje się do asm, który jest podobny do źródła. (std::unordered_map
jest to zwykle tablica mieszająca, alestd::map
zazwyczaj jest to czerwono-czarne drzewo binarne. Zaakceptowana odpowiedź skutecznie wykorzystujeangle%360 / 30
jako doskonałą funkcję mieszającą dla kątów (po replikacji kilku wpisów, a odpowiedź Bijaya unika tego nawet z przesunięciem)).lower_bound
na posortowanej tablicy. Byłoby to o wiele bardziej wydajne niżmap
.this->_car.getAbsoluteAngle()
zmiennej tmp i usunięcie zbędnego porównania z każdej zif()
klauzul OP (sprawdzenie, czy coś już pasowałoby poprzedni if ()). Lub użyj sugestii posortowanej tablicy @ wilx.Utwórz tablicę, której każdy element jest powiązany z blokiem o 30 stopniach:
Następnie możesz indeksować tablicę z kątem / 30:
Nie są wymagane żadne porównania ani rozgałęzienia.
Wynik jest jednak nieco odbiegający od oryginału. Wartości na granicach, tj. 30, 60, 120 itd. Są umieszczane w kolejnej kategorii. Na przykład w oryginalnym kodzie prawidłowe wartości
UP_RIGHT
to 31 do 60. Powyższy kod przypisuje 30 do 59 doUP_RIGHT
.Możemy to obejść, odejmując 1 od kąta:
To daje nam teraz
RIGHT
za 30,UP_RIGHT
za 60 itd.W przypadku 0 wyrażenie staje się
(-1 % 360) / 30
. Jest to ważne, ponieważ-1 % 360 == -1
i-1 / 30 == 0
, więc nadal otrzymujemy indeks 0.Sekcja 5.6 standardu C ++ potwierdza to zachowanie:
EDYTOWAĆ:
Pojawiło się wiele pytań dotyczących czytelności i łatwości utrzymania takiej konstrukcji. Odpowiedź udzielona przez motoDrizzt jest dobrym przykładem uproszczenia oryginalnej konstrukcji, która jest łatwiejsza w utrzymaniu i nie jest tak „brzydka”.
Rozszerzając jego odpowiedź, oto kolejny przykład wykorzystujący operator trójskładnikowy. Ponieważ każdy przypadek w oryginalnym poście jest przypisywany do tej samej zmiennej, użycie tego operatora może jeszcze bardziej zwiększyć czytelność.
źródło
Ten kod nie jest brzydki, jest prosty, praktyczny, czytelny i łatwy do zrozumienia. Zostanie wyodrębniony własną metodą, więc na co dzień nikt nie będzie musiał się nim zajmować. I na wypadek, gdyby ktoś musiał to sprawdzić - może dlatego, że debuguje Twoją aplikację pod kątem problemu w innym miejscu - jest to tak łatwe, że zrozumienie kodu i tego, co robi, zajmie mu dwie sekundy.
Gdybym robił taki debug, byłbym szczęśliwy, gdyby nie musiał spędzać pięciu minut na próbach zrozumienia, co robi twoja funkcja. Pod tym względem wszystkie inne funkcje zawodzą całkowicie, ponieważ zmieniają prostą, pozbawioną błędów procedurę, w skomplikowany bałagan, który ludzie podczas debugowania będą zmuszeni do dogłębnej analizy i testowania. Jako kierownik projektu bardzo bym się zdenerwował, gdy programista podjął proste zadanie i zamiast wdrożyć je w prosty, nieszkodliwy sposób, tracę czas na implementację w zbyt skomplikowany sposób. Po prostu pomyśl cały czas, kiedy traciłeś czas na myślenie o tym, a potem przychodziłeś do SO z pytaniem, a wszystko po to, aby pogorszyć konserwację i czytelność rzeczy.
To powiedziawszy, w Twoim kodzie jest typowy błąd, który sprawia, że jest on mniej czytelny, oraz kilka ulepszeń, które możesz zrobić dość łatwo:
Umieść to w metodzie, przypisz zwróconą wartość do obiektu, zwiń metodę i zapomnij o niej na resztę wieczności.
PS jest inny błąd powyżej progu 330, ale nie wiem, jak chcesz go leczyć, więc w ogóle go nie naprawiłem.
Późniejsza aktualizacja
Zgodnie z komentarzem możesz nawet pozbyć się innych, jeśli w ogóle:
Nie zrobiłem tego, bo czuję, że w pewnym momencie staje się to tylko kwestią własnych preferencji, a zakres mojej odpowiedzi polegał (i jest) na nadanie innej perspektywy Twojej trosce o „brzydotę kodu”. W każdym razie, jak powiedziałem, ktoś w komentarzach zwrócił na to uwagę i myślę, że warto to pokazać.
źródło
else if
,if
wystarczy.else if
. Uważam, że warto rzucić okiem na blok kodu i zobaczyć, że jest to drzewo decyzyjne, a nie grupa niepowiązanych ze sobą instrukcji. Tak,else
lubbreak
nie są potrzebne kompilatorowi po areturn
, ale są przydatne dla człowieka, który patrzy na kod.elseif
/elsif
, albo technicznie używasz bloku z jednym wyrażeniem, który tak się zaczynaif
, tak jak tutaj. Krótki przykład tego, o czym myślę, i o czym myślę: gist.github.com/IMSoP/90bc1e9e2c56d8314413d7347e76532aelse
zmuszanie do tego, jest to kiepski przewodnik po stylu, którego nie można rozpoznaćelse if
jako wyraźnego stwierdzenia. Zawsze używałbym nawiasów klamrowych, ale nigdy nie pisałbym takiego kodu, jak pokazałem w istocie.W pseudokodzie:
Tak, mamy
0-60
,60-90
,90-150
, ... jako kategoriach. W każdym kwadrancie o 90 stopniach jedna część ma 60, a jedna 30. Więc teraz:Użyj indeksu w tablicy zawierającej wyliczenia w odpowiedniej kolejności.
źródło
źródło
Ignorując pierwszy,
if
który jest trochę szczególny, pozostałe wszystkie mają ten sam wzór: min, max i kierunek; pseudo kod:Stworzenie tego prawdziwego C ++ może wyglądać następująco:
Zamiast pisać kilka
if
zdań, po prostu przejrzyj różne możliwości:( Inną opcją jest
throw
raczej wyjątek niżreturn
ingNONE
).Które następnie nazwałbyś:
Ta technika jest znana jako programowanie oparte na danych . Oprócz pozbycia się kilku
if
znaków, pozwoliłoby to na łatwe dodawanie kolejnych kierunków (np. NNW) lub zmniejszanie liczby (w lewo, w prawo, w górę, w dół) bez ponownego przetwarzania innego kodu.(Zajmowanie się pierwszym specjalnym przypadkiem pozostawiamy jako „ćwiczenie dla czytelnika”. :-))
źródło
if(angle <= angleRange.max)
ale +1 do korzystania z funkcji C ++ 11, takich jakenum class
.Chociaż proponowane warianty oparte na tabeli przeglądowej
angle / 30
są prawdopodobnie preferowane, tutaj jest alternatywa, która wykorzystuje kodowane na stałe wyszukiwanie binarne, aby zminimalizować liczbę porównań.źródło
Jeśli naprawdę chcesz uniknąć powielania, możesz to wyrazić wzorem matematycznym.
Przede wszystkim załóżmy, że używamy Enum @ Geek
Teraz możemy obliczyć wyliczenie za pomocą matematyki całkowitoliczbowej (bez potrzeby stosowania tablic).
Jak wskazuje @motoDrizzt, zwięzły kod niekoniecznie jest kodem czytelnym. Ma tę niewielką zaletę, że wyrażanie tego jako matematyki wyraźnie wskazuje, że niektóre kierunki obejmują szerszy łuk. Jeśli chcesz pójść w tym kierunku, możesz dodać kilka potwierdzeń, które pomogą zrozumieć kod.
Po dodaniu potwierdzeń dodałeś duplikację, ale powielanie w potwierdzeniach nie jest takie złe. Jeśli masz niespójne twierdzenie, wkrótce się o tym dowiesz. Potwierdzenia można skompilować poza wydaną wersją, aby nie nadużywać rozpowszechnianego pliku wykonywalnego. Niemniej jednak to podejście jest prawdopodobnie najbardziej odpowiednie, jeśli chcesz zoptymalizować kod, a nie tylko uczynić go mniej brzydkim.
źródło
Spóźniłem się na imprezę, ale moglibyśmy użyć flag wyliczenia i kontroli zasięgu, aby zrobić coś porządnego.
sprawdzanie (pseudokod) przy założeniu, że kąt jest absolutny (między 0 a 360):
źródło