2015-08-04 11 views
24

Pracuję nad grą, a ja znajduję się bardzo często sprawdzając, że pewne ilości są w granicach wskaźników przyjętych przez wektor, który reprezentuje mój świat:Prosty sposób na sprawdzenie dużej liczby podobnych warunków?

if(a >= 0 && a < 16 && b >= 0 && b < 16 && c >= 0 && c < 16 && 
    d >= 0 && d < 16 && e >= 0 && e < 16) 
{ 
    //do things with vector[a][b][c][d][e] 
} 

Często trzeba sprawdzić jeszcze bardziej warunki niż to. Czy istnieje sposób, aby sprawić, by te kontrole były bardziej zwięzłe i/lub łatwiejsze do odczytania?

Czy istnieje sposób, w jaki mogę całkowicie uniknąć przeprowadzania kontroli? Wektor to 16x16x16x16x16; czy mogę zrobić tak, że gdybym miał dać mu 16 jako indeks, nie zrobiłoby to nic zamiast segfault?

+9

Napisz funkcję 'inRange()' i wywołaj ją we wszystkich zmiennych? – yizzlez

+5

To jest problem x-y. Znacznie lepiej jest połączyć 4 punkty i 4 obligacje w coś takiego jak "QRect" i użyć metody takiej jak 'bool QRect :: zawiera (const QPoint & point, bool proper = false) const' http://doc.qt.io/ qt-5/qrect.html # zawiera – user3528438

+1

Po pierwsze, nie uważam, że mądrze jest "po prostu nie robić nic, a nie segfault", jeśli indeks jest poza zakresem. Prostym powodem jest to, że kod wywołujący może założyć, że ma poprawny indeks, co oznacza, że ​​błąd rozprzestrzenia się za pośrednictwem programu. Jeśli jednak to nie ma znaczenia, można zamienić wektor wektorów 16x16x16x16x16 na pojedynczy wektor i obliczyć indeks przy użyciu przesunięcia bitowego, który jest stosunkowo szybki i unika się narzutu skakania przez tablice. BTW, jeśli rozmiar 16 jest ustalony, każde 'i' gdzie' i! = (I & 15) 'jest nieprawidłowe. Wreszcie, użycie opcji 'vector :: at()' będzie opcją. –

Odpowiedz

38

Mogłeś napisać o zmiennej liczbie argumentów check funkcji :

bool check(int a) { 
    return 0 <= a && a < 16; 
} 

template<typename... Args> 
bool check(int a, Args... args) { 
    return check(a) && check(args...); 
} 

Można go używać jak check(a, b, c, d, e, ...). Ma także tę zaletę, że jest w stanie przyjąć dowolną liczbę warunków.

Here's a demo

+1

Porównanie można zoptymalizować bardziej w ten sposób [Najszybsza metoda w C, aby określić, czy liczba całkowita jest pomiędzy dwiema liczbami całkowitymi (włącznie) ze znanymi zestawami wartości] (http://stackoverflow.com/a/17095534/995714) 'return ((unsigned) a <= 16) && check (args ...); ' –

+1

Usunąłeś duplikację, pisząc swoją drugą funkcję w następujący sposób:' return check (a) && check (args ...); ' – Joel

+12

@ LưuVĩnhPhúc To naprawdę bardzo dobra optymalizacja wydajności. Dlatego twórcy kompilatorów stosują go przez bardzo, bardzo długi czas i bez konieczności zaciemniania kodu. – Voo

11

Celem funkcji jest możliwość ponownego użycia. Jeśli często piszesz kilka długich wyrażeń lub grup zdań, być może nadszedł czas, aby je zrestrukturyzować.

W tym przypadku, chciałbym napisać prostą funkcję do zrobienia sprawdzanie bounds:

bool isInBounds(int a, int b, int c, int d, int e) 
{ 
    return a >= 0 && a < 16 && 
      b >= 0 && b < 16 && 
      c >= 0 && c < 16 && 
      d >= 0 && d < 16 && 
      e >= 0 && e < 16; 
} 

następnie używać go zamiast długiej warunkiem:

if (isInBounds(a, b, c, d, e)) 
{ 
    // do things with array[a][b][c][d][e] 
} 
+0

Podczas sprawdzania, czy zmienne mieszczą się w pewnym zakresie, generalnie łatwiej jest je odczytać, jeśli napiszesz warunek jako 0 <= a && a <16 (to sprawia, że ​​wygląda on podobnie do 0 <= a <16) –

+0

@RichSmith To jest dobra sugestia, chociaż osobiście zawsze lubię widzieć najpierw nazwę zmiennej i stałą sekundę. To naprawdę jest opinia, więc cokolwiek ludzie chcą użyć. – Jashaszun

9

można przechowywać zmienne jako elementów w std::vector zamiast oddzielnych variabes jak ten:

bool test(const std::vector<int>& values) 
{ 
    for(auto v: values) 
     if(v < 0 || v >= 16) 
      return false; 
    return true; 
} 

Alternatywnie, jeśli używasz C++11 lub później można użyć std::all_of:

if(std::all_of(std::begin(values), std::end(values), 
    [](int i){ return i >= 0 && i < 16; })) 
{ 
    // do stuff with values 
} 

W takim przypadku można również użyć numeru std::array .

5

Można połączyć 5 liczb całkowitych tworzących indeks w jeden std::array lub własną klasę.

using Index5 = std::array<int, 5>; 

Następnie można napisać funkcję, jak:

bool contains(Index5 bounds, Index5 point) { 
    for (Index5::size_type d = 0; d != bounds.size(); ++d) { 
     if ((unsigned)point[d] > bounds[d]) // using the trick mentioned in comments 
      return false; 
    } 
    return true; 
} 

następnie używać go tak:

auto bounds = Index5{16, 16, 16, 16, 16}; 
auto point = Index5{a, b, c, d, e}; 

if (contains(bounds, point)) { 
    // do things with point 
} 

Generalnie chciałbym zaproponować użyciu coś jak Index5 zamiast zarządzania pięć liczb całkowitych.

13

Oto kompaktowy i skuteczny sposób na sprawdzenie. Zakłada arytmetyczne uzupełnienie dwójki.

bool IsInBounds(int a, int b, int c, int d, int e) 
{ 
    // Make sure only bits 0-3 are set (i.e. all values are 0-15) 
    return ((a | b | c | d | e) & ~0xf) == 0; 
} 

Działa od stwierdzenia, że ​​wszystkie wartości spoza zakresu 0-15 wszystkie mają ustawiony bit, który nie jest jednym z czterech najmniej znaczących nich, a wszystkie wartości wewnątrz zakresu nie.

Oczywiście warto korzystać z tego rodzaju optymalizacji, jeśli zyski w zakresie wydajności przeważają nad utratą czytelności kodu.

+1

@mucaho: który komputer nie ma arytmetycznej uzupełnień dwójki? – rkosegi

+0

@rkosegi Prawie brak zgodnie z tym [SE question] (http://stackoverflow.com/questions/161797/is-ones-complement-a-real-world-issue-or-just-a-historical-one) – mucaho

1

Jeżeli ilości a, b, c, d i e są czymś, co występuje razem dość często i wszyscy muszą pozostać w granicach swojego „świata” (np reprezentują one „państwo” czegoś w tamten świat) to może mieć sens zdefiniowanie klasy, której głównym celem jest utrzymywanie jednego "stanu" składającego się z tych pięciu ilości.

Następnie upewnij się, że jeśli kod kiedykolwiek stara się przechowywać wartości w obiekcie tej klasy, które nie mieszczą się w granicach, coś rozsądnego (nie wysypać) dzieje zamiast i żadnych out-of-granice wartości są tam zawsze przechowywane. ten sposób obiekt tej klasy jest bezpiecznie przejść do dowolnej funkcji wymaga a, b, c, d i e się w ryzach, i nie ma potrzeby takiej funkcji robić granice sprawdzanie na tych pięciu wartościach.

Powiązane problemy