У меня такой код:
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;
Я хочу избежать if
цепочки s; это действительно некрасиво. Есть ли другой, возможно, более чистый способ написать это?
c++
if-statement
Oraekia
источник
источник
this->_car.getAbsoluteAngle()
один раз перед всем каскадом.this
(this->
) не нужно и на самом деле ничего хорошего для читабельности не делает ..>
тестов код был бы намного менее уродливым ; они не нужны, поскольку каждый из них уже был протестирован (в обратном направлении) в предыдущемif
утверждении.else if
.Ответы:
Вот как бы я это сделал. (Согласно моему предыдущему комментарию).
источник
q = a/b
иr = a%b
thenq * b + r
должны быть равныa
. Таким образом, в C99 допустимо, чтобы остаток был отрицательным. BorgLeader, вы можете решить проблему с помощью(((angle % 360) + 360) % 360) / 30
.GetDirectionForAngle
это то, что я предлагаю в качестве замены для каскада if / else, они оба O (1) ...Вы можете использовать
map::lower_bound
и сохранять верхнюю границу каждого угла на карте.Рабочий пример ниже:
источник
table[angle%360/30]
ответов, потому что он дешевый и безотказный. Намного дешевле, чем цикл поиска по дереву, если он компилируется в asm, похожий на исходный. (std::unordered_map
обычно представляет собой хеш-таблицу, ноstd::map
обычно представляет собой красно-черное двоичное дерево. Принятый ответ эффективно используетсяangle%360 / 30
в качестве идеальной хеш-функции для углов (после репликации пары записей, а ответ Биджея даже позволяет избежать этого со смещением)).lower_bound
отсортированный массив. Это было бы намного эффективнее, чемmap
.this->_car.getAbsoluteAngle()
с помощью tmp var и удаления избыточного сравнения из каждого предложения OPif()
(проверка того, что уже соответствовало предыдущий if ()). Или используйте предложение отсортированного массива @ wilx.Создайте массив, каждый элемент которого связан с блоком 30 градусов:
Затем вы можете проиндексировать массив с углом / 30:
Никаких сравнений или ветвлений не требуется.
Однако результат немного отличается от оригинала. Значения на границах, то есть 30, 60, 120 и т. Д., Помещаются в следующую категорию. Например, в исходном коде допустимые значения от
UP_RIGHT
31 до 60. Приведенный выше код присваивает от 30 до 59UP_RIGHT
.Мы можем обойти это, вычтя 1 из угла:
Теперь мы получаем
RIGHT
30,UP_RIGHT
60 и т. Д.В случае 0 выражение становится
(-1 % 360) / 30
. Это верно, потому что-1 % 360 == -1
и-1 / 30 == 0
, поэтому мы все еще получаем индекс 0.Раздел 5.6 стандарта C ++ подтверждает это поведение:
РЕДАКТИРОВАТЬ:
Было поднято много вопросов относительно удобочитаемости и ремонтопригодности такой конструкции. Ответ, данный motoDrizzt, является хорошим примером упрощения исходной конструкции, которая более удобна в обслуживании и не так «уродлива».
Продолжая его ответ, вот еще один пример использования тернарного оператора. Поскольку каждый случай в исходном сообщении присваивается одной и той же переменной, использование этого оператора может еще больше повысить удобочитаемость.
источник
Этот код не уродлив, он простой, практичный, читаемый и легкий для понимания. Он будет изолирован в своем собственном методе, поэтому никому не придется иметь дело с ним в повседневной жизни. И на всякий случай, если кто-то должен это проверить - может быть, потому, что он отлаживает ваше приложение на предмет проблемы в другом месте - это настолько просто, что ему потребуется две секунды, чтобы понять код и то, что он делает.
Если бы я делал такую отладку, я был бы счастлив, если бы мне не пришлось тратить пять минут на попытки понять, что делает ваша функция. В этом отношении все другие функции полностью выходят из строя, поскольку они изменяют простую, забытую и избавляющую от ошибок процедуру, в сложном беспорядке, который люди при отладке будут вынуждены глубоко проанализировать и протестировать. Я, как руководитель проекта, сильно расстроился бы, если бы разработчик взял простую задачу и вместо того, чтобы реализовать ее простым и безвредным способом, тратит время на реализацию ее слишком сложным способом. Подумайте, сколько времени вы потратили на размышления об этом, а затем пришли к SO с просьбой, и все просто ради ухудшения обслуживания и читабельности.
Тем не менее, в вашем коде есть распространенная ошибка, которая делает его менее читабельным, и пару улучшений, которые вы можете легко сделать:
Поместите это в метод, назначьте возвращаемое значение объекту, сверните метод и забудьте о нем на всю оставшуюся вечность.
PS есть еще одна ошибка, превышающая порог 330, но я не знаю, как вы хотите ее лечить, поэтому я ее вообще не исправил.
Позднее обновление
Согласно комментарию, вы даже можете избавиться от else, если вообще:
Я не делал этого, потому что чувствую, что в какой-то момент это становится просто вопросом личных предпочтений, и объем моего ответа заключался (и есть) в том, чтобы дать другую точку зрения на вашу озабоченность «уродством кода». Во всяком случае, как я уже сказал, кто-то указал на это в комментариях, и я думаю, что есть смысл показать это.
источник
else if
, этогоif
достаточно.else if
. Я считаю полезным иметь возможность взглянуть на блок кода и увидеть, что это дерево решений, а не группа не связанных между собой операторов. Да,else
илиbreak
они не нужны компилятору после areturn
, но они полезны для человека, который смотрит на код.elseif
/elsif
, либо вы технически используете блок из одного оператора, который начинаетсяif
, как здесь. Быстрый пример того, о чем я думаю, и о чем я думаю: gist.github.com/IMSoP/90bc1e9e2c56d8314413d7347e76532aelse
заставляет вас делать это, это плохое руководство по стилю, которое не распознаетсяelse if
как отдельное утверждение. Я всегда использовал фигурные скобки, но я никогда не стал бы писать такой код, как я показал в своей сути.В псевдокоде:
Итак, у нас есть
0-60
,60-90
,90-150
... как категории. В каждом квадранте с 90 градусами одна часть - 60, другая - 30. Итак, теперь:Используйте индекс в массиве, содержащем перечисления в соответствующем порядке.
источник
источник
Игнорируя ваше первое,
if
что является немного частным случаем, все остальные следуют одному и тому же шаблону: min, max и direction; псевдокод:Реальный C ++ может выглядеть так:
Теперь, вместо того, чтобы писать кучу
if
s, просто переберите свои различные возможности:(
throw
ИНГ исключение , а неreturn
ИнгNONE
другой вариант).Что бы вы затем назвали:
Этот метод известен как программирование, управляемое данными . Помимо избавления от множества
if
s, это позволит вам легко использовать добавление дополнительных направлений (например, NNW) или уменьшать число (влево, вправо, вверх, вниз) без переделки другого кода.(Работа с вашим первым частным случаем остается «упражнением для читателя». :-))
источник
if(angle <= angleRange.max)
+1 для использования таких функций C ++ 11, какenum class
.Хотя предлагаемые варианты, основанные на таблице поиска
angle / 30
, вероятно, предпочтительны, вот альтернатива, которая использует жестко закодированный двоичный поиск, чтобы минимизировать количество сравнений.источник
Если вы действительно хотите избежать дублирования, вы можете выразить это в виде математической формулы.
Прежде всего, предположим, что мы используем Enum от @ Geek.
Теперь мы можем вычислить перечисление, используя целочисленную математику (без необходимости в массивах).
Как указывает @motoDrizzt, краткий код не обязательно читаемый. У него есть небольшое преимущество в том, что его математическое выражение явно указывает на то, что некоторые направления покрывают более широкую дугу. Если вы хотите пойти в этом направлении, вы можете добавить несколько утверждений, которые помогут понять код.
Добавив утверждения, вы добавили дублирование, но дублирование в утверждениях не так уж и плохо. Если у вас есть непоследовательное утверждение, вы скоро узнаете. Утверждения могут быть скомпилированы вне версии выпуска, чтобы не раздувать исполняемый файл, который вы распространяете. Тем не менее, этот подход, вероятно, наиболее применим, если вы хотите оптимизировать код, а не просто сделать его менее уродливым.
источник
Я опаздываю на вечеринку, но мы могли бы использовать флаги перечисления и проверки диапазона, чтобы сделать что-нибудь аккуратное.
проверка (псевдокод), предполагая абсолютный угол (от 0 до 360):
источник