2015-08-04 2 views
24

Я работаю над игрой, и я нахожу себя очень часто проверяя, что некоторые величины находятся в пределах показателей, принятых вектором, который представляет мой мир:Простой способ проверить большое количество подобных условий?

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] 
} 

мне часто приходится проверять еще чем это. Есть ли способ, который я могу сделать эти проверки более краткими и/или более легкими для чтения?

В качестве альтернативы, существует ли способ, которым я могу избежать проверки полностью? Вектор 16x16x16x16x16; могу ли я сделать так, чтобы, если бы я дал ему 16 в качестве индекса, он ничего не сделал бы, а не segfault?

+9

Напишите функцию 'inRange()' и вызовите ее для всех переменных? – yizzlez

+5

Это проблема x-y. Гораздо лучше объединить 4 точки и 4 связывания в нечто вроде 'QRect' и использовать метод типа' bool QRect :: contains (const QPoint & point, bool proper = false) const' http://doc.qt.io/ qt-5/qrect.html # содержит – user3528438

+1

Во-первых, я не думаю, что разумно «просто ничего не делать, а не segfault», если индекс выходит за пределы диапазона. Простая причина заключается в том, что вызывающий код может считать, что он имеет правильный индекс, что означает, что ошибка распространяется через программу. Однако, если это не имеет значения, вы можете заменить вектор векторов 16x16x16x16x16 на один вектор и вычислить индекс, используя бит-сдвиг, что относительно быстро и позволяет избежать накладных расходов при переходе через массивы. BTW, если размер 16 фиксирован, любое 'i', где' i! = (I & 15) 'является недопустимым. Наконец, использование опции 'vector :: at()' будет вариантом. –

ответ

38

Вы можете написать VARIADIC check функцию :

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

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

Вы можете использовать его как check(a, b, c, d, e, ...). Это также имеет то преимущество, что вы можете принять любое количество условий.

Here's a demo

+1

сравнение может быть оптимизировано больше с помощью этого способа [Самый быстрый способ в C определить, является ли целое число между двумя целыми числами (включительно) с известными наборами значений] (http://stackoverflow.com/a/17095534/995714) 'return ((без знака) a <= 16) && check (args ...); ' –

+1

Вы удаляете некоторое дублирование, записывая вторую функцию следующим образом:' return check (a) && check (args ...); ' – Joel

+12

@ LưuVĩnhPhúc Это очень хорошая оптимизация производительности. Именно поэтому авторы компиляторов применяют его очень и очень долго, и вам не придется запутывать ваш код. – Voo

11

Точка функций повторного использования. Если вы неоднократно набираете несколько длинных выражений или групп утверждений, возможно, настало время реорганизовать его.

В этом случае, я хотел бы написать простую функцию, чтобы сделать проверку ограничений:

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; 
} 

Затем использовать его вместо вашего длительного состояния:

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

Если вы проверяете, находятся ли переменные в определенном диапазоне, это обычно легче читать, если вы записываете условие как 0 <= a && a <16 (это делает его очень похожим на 0 <= a <16) –

+0

@RichSmith Это Хорошее предложение, хотя лично мне всегда нравится видеть имя переменной в первую очередь и постоянную секунду. Это действительно мнение, так что все, что люди хотят использовать. – Jashaszun

9

Вы можете хранить переменные в качестве элементов в std::vector, а не отдельных variabes, как это:

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

В качестве альтернативы, если вы используете C++11 или более поздней версии вы можете использовать std::all_of:

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

В этом случае вы также можете использовать std::array ,

5

Вы можете комбинировать 5 целых чисел, составляющих ваш индекс, в один std::array или свой собственный класс.

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

Тогда вы можете написать функцию, как:

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; 
} 

Затем использовать его как это:

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

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

Вообще, я предложил бы использовать что-то вроде Index5 вместо управления пять целых чисел.

13

Вот компактный и эффективный способ проверки. Он предполагает две арифметические операции дополнения.

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; 
} 

Это работает, отметив, что все значения вне диапазона 0-15 все есть набор бит, который не один из четырех не менее значимых, и всех значений внутри диапазона нет.

Конечно, стоит использовать такую ​​оптимизацию, только если выигрыш в эффективности перевешивает потерю удобочитаемости кода.

+1

@mucaho: на каком ПК нет арифметики дополнений? – rkosegi

+0

@rkosegi Почти никто в соответствии с этим [вопрос SE] (http://stackoverflow.com/questions/161797/is-ones-complement-a-real-world-issue-or-just-a-historical-one) – mucaho

1

Если величины a, b, c, d и e это то, что происходит вместе довольно часто, и все должны оставаться в пределах вашего «мира» (например, они представляют собой «состояние» что-то в этот мир) , тогда может иметь смысл определить класс, основной целью которого является , чтобы провести одно «состояние», состоящее из этих пяти величин.

Затем убедитесь, что если какой-либо код либо пытается хранить значения в объекте этого класса, которые не являются в пределах, то разумное (не Segfault) происходит вместо этого, и не недоступный значения когда-либо хранятся там. Таким образом, объект этого класса безопасно перейти к любой функции, требует a, b, c, d и e быть в пределах границ, и нет никакой необходимости в такой функции, чтобы сделать границы проверки по этим пяти значениям.

Смежные вопросы