Minha pergunta é:
if (/* condition A */)
{
if(/* condition B */)
{
/* do action C */
}
else
/* ... */
}
else
{
/* do action C */
}
É possível escrever apenas o código de ação C uma vez em vez de duas vezes?
Como simplificá-lo?
Respostas:
Seu primeiro passo nesse tipo de problema é sempre criar uma tabela lógica.
Depois de fazer a mesa, a solução é clara.
Observe que essa lógica, embora mais curta, pode ser difícil para futuros programadores manterem.
fonte
B
tiver efeitos colaterais, a tabela lógica deve levar isso em consideração.A && !B
caso seja um no-op:!(A && !B)
é equivalente ao!A || B
que significa que você pode fazerif (!A || B) { /* do action C */ }
e evitar um bloco vazio.if (A && !B)
é realmente difícil para futuros programadores manter, então não há realmente como ajudá-los.Você tem duas opções:
Escreva uma função que execute a "ação C".
Reorganize sua lógica para que você não tenha tantas instruções if aninhadas. Pergunte a si mesmo que condições causam a "ação C". Parece-me que acontece quando a "condição B" é verdadeira ou a "condição A" é falsa. Podemos escrever isso como "NÃO A OU B". Traduzindo isso para o código C, obtemos
Para aprender mais sobre esse tipo de expressão, sugiro pesquisar "álgebra booleana", "lógica de predicados" e "cálculo de predicados". Estes são tópicos matemáticos profundos. Você não precisa aprender tudo, apenas o básico.
Você também deve aprender sobre "avaliação de curto-circuito". Por esse motivo, a ordem das expressões é importante para duplicar exatamente sua lógica original. Embora
B || !A
seja logicamente equivalente, usar isso como condição irá executar a "ação C" quandoB
for verdadeira, independentemente do valor deA
.fonte
...
é . Se não for nada (ou seja, “faça C se essas condições forem atendidas; caso contrário, não faça nada”), essa é claramente a solução superior, pois aelse
afirmação pode ser deixada de fora por completo.Você pode simplificar a declaração assim:
Caso contrário, coloque o código para 'C' em uma função separada e chame-o:
fonte
if (!A || B)
B || !A
terá como resultado atrue
apenas seB
étrue
, sem realmente verificandoA
devido a um curto-circuitoA
eB
representa.Em um idioma com correspondência de padrões, você pode expressar a solução de uma maneira que reflita mais diretamente a tabela de verdade na resposta da PerguntaC.
Se você não estiver familiarizado com a sintaxe, cada padrão é representado por um | seguido pelos valores para combinar com (a, b) e o sublinhado é usado como curinga para significar "quaisquer outros valores". Como o único caso em que queremos fazer algo diferente da ação c é quando a é verdadeiro eb é falso, declaramos explicitamente esses valores como o primeiro padrão (verdadeiro, falso) e, então, fazemos o que deve ser feito nesse caso. Em todos os outros casos, passamos ao padrão "curinga" e executamos a ação c.
fonte
A declaração do problema:
descreve a implicação : A implica B , uma proposição lógica equivalente a
!A || B
(como mencionado em outras respostas):fonte
inline
para C econstexpr
também para C ++?Ugh, isso também me deixou tropeçado, mas, como indicado pelo Code-Apprentice , garantimos que precisamos
do action C
ou executamos oelse
bloco aninhado , portanto, o código pode ser simplificado para:É assim que chegamos aos 3 casos:
do action C
na lógica da sua pergunta é necessáriocondition A
econdition B
deve sertrue
- Nessa lógica, se atingirmos o 2º termo noif
demonstrativo, sabemos quecondition A
é,true
portanto, tudo o que precisamos avaliar é o seguintecondition B
:true
else
bloco aninhado na lógica da sua pergunta precisavacondition A
sertrue
econdition B
serfalse
- A única maneira de alcançar oelse
bloco nessa lógica seria secondition A
fossetrue
econdition B
fossefalse
else
bloco externo na lógica da sua pergunta precisacondition A
serfalse
- Nesta lógica, secondition A
for falso, tambémdo action C
Suportes ao Code-Apprentice por me endireitar aqui. Sugiro aceitar sua resposta , pois ele a apresentou corretamente sem editar: /
fonte
B
será avaliado apenas se!A
for falso. Portanto, ambos devem falhar para executar aselse
instruções.!A || B
é falso exatamente quando ambos!A
eB
são falsos. Portanto,A
será verdade quando oelse
executa. Não há necessidade de reavaliarA
.No conceito de lógica, você pode resolver esse problema da seguinte maneira:
Como um problema comprovado, isso resulta em
f = !a + b
. Existem algumas maneiras de provar o problema, como tabela de verdade, mapa de Karnaugh e assim por diante.Portanto, em idiomas baseados em C, você pode usar o seguinte:
PS: Karnaugh Map também é usado para séries mais complicadas de condições. É um método de simplificar expressões de álgebra booleana.
fonte
Embora já existam boas respostas, pensei que essa abordagem poderia ser ainda mais intuitiva para alguém que é novo na álgebra booleana do que para avaliar uma tabela da verdade.
A primeira coisa que você quer fazer é procurar, sob quais condições você deseja executar C. É esse o caso quando
(a & b)
. Também quando!a
. Então você tem(a & b) | !a
.Se você quiser minimizar, pode continuar. Assim como na aritmética "normal", você pode multiplicar.
(a & b) | !a = (a | !a) & (b | !a)
. a | ! a é sempre verdade, então você pode simplesmente riscar, o que deixa o resultado minimizado:b | !a
. Caso a ordem faça diferença, porque você deseja marcar b apenas se! A for verdadeiro (por exemplo, quando! A é uma verificação de ponteiro nulo eb é uma operação no ponteiro como @LordFarquaad apontado em seu comentário), você pode quer mudar os dois.O outro caso (/ * ... * /) é sempre será executado quando c não for executado, portanto, podemos apenas colocá-lo no caso else.
Também vale mencionar que provavelmente faz sentido de qualquer maneira colocar a ação c em um método.
O que nos deixa com o seguinte código:
Dessa forma, você também pode minimizar os termos com mais operandos, o que rapidamente fica feio com as tabelas verdadeiras. Outra boa abordagem são os mapas de Karnaugh. Mas não vou aprofundar isso agora.
fonte
Para tornar o código mais parecido com texto, use sinalizadores booleanos. Se a lógica for especialmente obscura, adicione comentários.
fonte
fonte
Eu extrairia C para um método e sairia da função o mais rápido possível em todos os casos.
else
cláusulas com uma única coisa no final quase sempre devem ser invertidas, se possível. Aqui está um exemplo passo a passo:Extrato C:
Inverta primeiro
if
para se livrar do primeiroelse
:Livre-se do segundo
else
:E então você pode perceber que os dois casos têm o mesmo corpo e podem ser combinados:
Coisas opcionais para melhorar seriam:
depende do contexto, mas se
!A || B
for confuso, extraia-o para uma ou mais variáveis para explicar a intençãoqualquer dos
C()
ouD()
é o caso não excepcional deve ir passado, por isso, seD()
é a exceção, então invertido aif
uma última vezfonte
O uso de sinalizadores também pode resolver esse problema
fonte