Eu tenho o seguinte código:
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;
Eu quero evitar a if
corrente s; é muito feio. Existe outra maneira, possivelmente mais limpa, de escrever isso?
c++
if-statement
Oraekia
fonte
fonte
this->_car.getAbsoluteAngle()
uma vez antes de toda a cascata.this
(this->
) não é necessária e realmente não faz nada de bom para a legibilidade.>
testes; eles não são necessários, uma vez que cada um deles já foi testado (na direção oposta) naif
instrução anterior .else if
.Respostas:
É assim que eu faria. (De acordo com meu comentário anterior).
fonte
q = a/b
er = a%b
entãoq * b + r
devem ser iguaisa
. Assim, é legal em C99 que um resto seja negativo. BorgLeader, você pode corrigir o problema com(((angle % 360) + 360) % 360) / 30
.GetDirectionForAngle
é o que estou propondo como uma substituição para a cascata if / else, ambos são O (1) ...Você pode usar
map::lower_bound
e armazenar o limite superior de cada ângulo em um mapa.Exemplo de trabalho abaixo:
fonte
table[angle%360/30]
respostas porque é barato e sem ramos. Muito mais barato do que um loop de busca em árvore, se ele compilar em uma forma semelhante à fonte. (std::unordered_map
geralmente é uma tabela hash, masstd::map
é tipicamente uma árvore binária vermelha e preta. A resposta aceita usa efetivamenteangle%360 / 30
como uma função hash perfeita para ângulos (depois de replicar algumas entradas, e a resposta de Bijay até evita isso com um deslocamento)).lower_bound
em uma matriz classificada. Isso seria muito mais eficiente do que amap
.this->_car.getAbsoluteAngle()
com uma var tmp e removendo a comparação redundante de cada uma dasif()
cláusulas do OP (verificando por algo que já teria correspondido o precedente if ()). Ou use a sugestão de array ordenado de @wilx.Crie uma matriz, cada elemento associado a um bloco de 30 graus:
Então você pode indexar a matriz com o ângulo / 30:
Nenhuma comparação ou ramificação necessária.
O resultado, entretanto, está um pouco diferente do original. Os valores nas bordas, ou seja, 30, 60, 120, etc. são colocados na próxima categoria. Por exemplo, no código original, os valores válidos de
UP_RIGHT
são 31 a 60. O código acima atribui 30 a 59 aUP_RIGHT
.Podemos contornar isso subtraindo 1 do ângulo:
Isso agora nos dá
RIGHT
30,UP_RIGHT
60 etc.No caso de 0, a expressão se torna
(-1 % 360) / 30
. Isso é válido porque-1 % 360 == -1
e-1 / 30 == 0
, portanto, ainda temos um índice de 0.A seção 5.6 do padrão C ++ confirma este comportamento:
EDITAR:
Muitas questões foram levantadas em relação à legibilidade e manutenção de um construto como este. A resposta dada por motoDrizzt é um bom exemplo de simplificação da construção original que é mais sustentável e não é tão "feia".
Expandindo sua resposta, aqui está outro exemplo que faz uso do operador ternário. Como cada caso na postagem original é atribuído à mesma variável, o uso desse operador pode ajudar a aumentar a legibilidade ainda mais.
fonte
Esse código não é feio, é simples, prático, legível e fácil de entender. Ficará isolado em seu próprio método, então ninguém terá que lidar com isso no dia a dia. E apenas no caso de alguém ter que verificar - talvez porque ele está depurando seu aplicativo para um problema em outro lugar - é tão fácil que ele levará dois segundos para entender o código e o que ele faz.
Se eu estivesse fazendo esse tipo de depuração, ficaria feliz em não ter que gastar cinco minutos tentando entender o que sua função faz. A este respeito, todas as outras funções falham completamente, pois mudam uma rotina simples, esquecê-lo, livre de bugs, em uma bagunça complicada que as pessoas ao depurar serão forçadas a analisar e testar profundamente. Como gerente de projeto, ficaria muito chateado se um desenvolvedor pegasse uma tarefa simples e, em vez de implementá-la de uma maneira simples e inofensiva, perdesse tempo para implementá-la de uma forma complicada. Basta pensar o tempo todo que você perdeu pensando sobre isso, então chegando ao SO perguntando, e tudo apenas para piorar a manutenção e a legibilidade da coisa.
Dito isso, há um erro comum em seu código que o torna muito menos legível, e algumas melhorias que você pode fazer facilmente:
Coloque isso em um método, atribua o valor retornado ao objeto, recolha o método e esqueça-o pelo resto da eternidade.
PS, há outro bug acima do limite de 330, mas não sei como você deseja tratá-lo, então não o corrigi de jeito nenhum.
Atualização posterior
De acordo com o comentário, você pode até mesmo se livrar do else:
Não o fiz porque sinto que a certo ponto se torna apenas uma questão de preferências próprias, e o escopo da minha resposta foi (e é) dar uma perspectiva diferente à sua preocupação sobre a "feiura do código". Enfim, como eu disse, alguém apontou nos comentários e acho que faz sentido mostrar.
fonte
else if
,if
é o suficiente.else if
. Acho útil poder dar uma olhada em um bloco de código e ver que é uma árvore de decisão, em vez de um grupo de instruções não relacionadas. Sim,else
oubreak
não são necessários para o compilador após areturn
, mas são úteis para o humano que está olhando para o código.elseif
/ separadaelsif
, ou você está tecnicamente usando um bloco de uma instrução que começaif
, assim como aqui. Um exemplo rápido do que acho que você está pensando e do que estou pensando: gist.github.com/IMSoP/90bc1e9e2c56d8314413d7347e76532aelse
que o faz fazer isso, é um guia de estilo pobre que não reconheceelse if
como uma afirmação distinta. Eu sempre usaria colchetes, mas nunca escreveria um código assim, como mostrei em minha essência.Em pseudocódigo:
Então, nós temos
0-60
,60-90
,90-150
, ... como as categorias. Em cada quadrante com 90 graus, uma parte tem 60, uma parte tem 30. Então, agora:Use o índice em uma matriz contendo os enums na ordem apropriada.
fonte
fonte
Ignorando o primeiro,
if
que é um caso um tanto especial, os demais seguem exatamente o mesmo padrão: um mínimo, máximo e direção; pseudo-código:Tornar este C ++ real pode ser parecido com:
Agora, ao invés de escrever um monte de
if
s, apenas faça um loop sobre suas várias possibilidades:(
throw
Ing uma excepção em vez dereturn
ingNONE
é uma outra opção).Que você chamaria de:
Essa técnica é conhecida como programação baseada em dados . Além de eliminar um monte de
if
s, permitiria que você adicionasse facilmente mais direções (por exemplo, NNW) ou reduzisse o número (esquerda, direita, para cima, para baixo) sem retrabalhar outro código.(Lidar com seu primeiro caso especial é deixado como "um exercício para o leitor." :-))
fonte
if(angle <= angleRange.max)
mas +1 para usar recursos do C ++ 11 comoenum class
.Embora as variantes propostas com base em uma tabela de pesquisa
angle / 30
sejam provavelmente preferíveis, aqui está uma alternativa que usa uma pesquisa binária codificada para minimizar o número de comparações.fonte
Se você realmente deseja evitar a duplicação, pode expressar isso como uma fórmula matemática.
Em primeiro lugar, suponha que estamos usando @ Geek's Enum
Agora podemos calcular o enum usando matemática de inteiros (sem a necessidade de matrizes).
Como @motoDrizzt aponta, um código conciso não é necessariamente um código legível. Ele tem a pequena vantagem de expressá-lo como matemática torna explícito que algumas direções cobrem um arco mais amplo. Se você quiser seguir nessa direção, pode adicionar algumas declarações para ajudar a entender o código.
Tendo adicionado as declarações, você adicionou duplicação, mas a duplicação em declarações não é tão ruim. Se você tiver uma afirmação inconsistente, logo descobrirá. Asserts podem ser compilados fora da versão de lançamento para não sobrecarregar o executável que você distribui. No entanto, essa abordagem é provavelmente mais aplicável se você quiser otimizar o código em vez de apenas torná-lo menos feio.
fonte
Estou atrasado para a festa, mas poderíamos usar sinalizadores enum e checagem de alcance para fazer algo legal.
a verificação (pseudo-código), assumindo que o ângulo é absoluto (entre 0 e 360):
fonte