Reduzir a quantidade de if's

Pessoal, eu estava respondendo um problema simples em python que pedia para checar se um número estava contido em alguns intervalos e printar o resultado na tela, e caso o número não bata com 0 > x e x > 100 ele retorne uma mensagem de erro. Eu tentei usar o range mas não consegui fazer funcionar como eu queria, e resolvi escrever as definições matemáticas mesmo.

Só que, o código ficou com 4 if’s, teria um jeito mais performático de resolver esse problema ?

a = float(input())
if 0 <= a <= 25: 
    print('Intervalo [0,25]')
if 25 < a <= 50:
    print('Intervalo (25,50]')
if 50 < a <= 75:
    print('Intervalo (50,75]')
if 75 < a <= 100:
    print('Intervalo (75,100]')
if 0 > a or a > 100:
    print('Fora de intervalo')
1 curtida

Você pode usar o match case, caso esteja usando Python 3.10 ou superior

1 curtida

Olá! Eu não programo em python, mas em C nós usamos o switch case pra reduzir a quantidade de if’s, deve ter algum similar para o python

Analisando muito por cima, é notado no seu problema um salto de 25, para resolver isso em uma linha segue abaixo, um exemplo, mas será invalido em outras ocasiões…

a = float(input())

f = lambda x, y: 'Fora de intervalo' if 0 > a or a > 100 else f'Intervalo [{x},{y}]' if x < a <= y or a == 0 else f(x+25, y+25)
print(f(0, 25))
2 curtidas

Na prática você tem que verificar apenas 3 condições:

  • X está fora de todos os intervalos ?
  • X está no primeiro intervalo ?
  • X está em algum outro intervalo ?

Isso porque a saída de cada um desses casos é distinta:

  • ‘Fora de intervalo’
  • ‘Intervalo [0, 25]’
  • ‘Intervalo (min, max]’

E, além disso, o passo entre as fronteiras de cada intervalo está fixa (25).

Tente pensar em alguma estrutura de loop para resolver esse problema, rode um for gerando cada intervalo e verificando se o valor está dentro deste. Na prática você só vai precisar de 3 if e, caso esteja fazendo dentro de uma função, pode reduzir para 2 sem dificuldades (ao custo de um pouco mais de processamento, o que usualmente não é problema).

Eu fiz uma funçãozinha simples aqui para fazer isso, mas no seu caso, como é para estudo, não vou postar essa resposta ainda. Tente pensar um pouco nessa linha de ver as simetrias do seu problema (ter 3 formatos de saída desejados e passo fixo entre os limites de cada intervalo).

1 curtida

Beleza, vou dar uma olhada.

1 curtida

Esse problema é um exemplo básico de data binning ou data bucketing. Uma boa abordagem para este problema em particular seria tentar fazer algo como uma função matemática do tipo hash , que mapeie o valor para um dos intervalos:

Se o intervalo não incluir zero:

hash = (int(input()) + 25 - 1) // 25
if 1 <= hash <= 4:
    print(f'intervalo [{hash*25-25}, {hash*25}]')  
else:
    print("Fora de intervalo")

caso inclua:

a = int(input())
hash = (a + 25 - 1) // 25 if a != 0 else 1
if 1 <= hash <= 4:
    print(f'intervalo [{hash*25-25}, {hash*25}]')  
else:
    print("Fora de intervalo")
1 curtida
a = float(input())
lista_intervalos = []
def intervalos():
    for a in lista_intervalos:
        if a > 0 or a > 100:
            print('Fora do intervalo')
    

Algo nessa linha de raciocinio ?

1 curtida

Isso, está no caminho, vou te dar umas dicas que talvez te ajude na lógica:

Para checar se um valor está num intervalo específico, o Python dispõe de uma estrutura que facilita MUITO a leitura, ao invés de escrever x > min_val and x <= max_val, você pode escrever min_val < x <= max_val.

Outra coisa, veja que essa checagem, se o valor está dentro dos limites do seu problema, pode ficar fora do loop, já que esse não é um intervalo que será “rodado” no loop, tanto que você nem mesmo incluiu ele em lista_intervalos.

Pensa em algo mais ou menos assim:

a = float(input())
lista_intervalos = <Intervalos que você vai incluir>

def intervalos(x):
    if (x < 0) or (x > 100):
        print('Fora do intervalo')
        return 

    for intervalo in lista_intervalos:
        ...<restante do código>....

Agora, pensa nas outras duas condições, como você vai estruturar dentro desse for.

Pela descrição que ele deu do problema, aparentemente ele está aprendendo os primeiros passos na linguagem, por isso não abordei de forma matemática, acredito que esteja estudando mais essas estruturas lógicas e laços de repetição. Mas essa é uma saída bastante elegante!

1 curtida

Vou tentar hehe

Exatamente isso haha, eu entendi a solução do @romulopb, e achei muito boa, mas para o meu objeto de estudo aqui ela não vai me ajudar a entender os laços. Porém, foi muito bonita

1 curtida

Concordo, quis apresentar uma abordagem diferente, mais no objetivo de animar o @Physis_19 mostrando como um problema básico pode ser bem fascinante e se conectar com diversas áreas do saber.

2 curtidas

Não consegui andar muito para estruturar as outras condições mas eu pensei em algo assim

a = float(input())
lista_intervalos = [0 <= a <= 25, 25 < a <= 50, 50 < a <= 75, 75 < a <= 100]
def intervalos(a):
    for a in lista_intervalos:
        if (0 < a)  or (a > 100):
             print('Fora do intervalo')
             return
    for a in lista_intervalos: 
        if a in range (0,26):
            print('Intervalo [0,25]')
            return
    for a in range(25, 101, 25):
            return
            

Para checar a ultima condição se ele pertence a algum outro intervalo, eu pensei em usar um range que começa no 25 e vai pulando de 25 em 25 até chegar no 100, e se o valor estiver nesse range ele vai estar em algum intervalo. Mas não consegui realmente desenvolver muita coisa kkkkk

1 curtida

Se neste caso de ser somente pra treino de for-loop segue uma ideia, sem o uso de uma função…

a=float(input())
for x in range(0, 100, 25):
    if x < a <= x+25 or a == 0:
        print(f'Intervalo [{x}, {x+25}]')
        break
    elif 0 > a or a > 100:
        print('Fora do Intervalo')
        break
2 curtidas

Calma, vamos por partes…

Primeiro ponto é que está acontecendo uma confusão de variáveis ai… de forma global, a começa com um float, o valor que foi inserido, depois disso você transforma essa variável, ainda globalmente, numa lista de booleanos onde cada bool indica se o valor inserido faz parte ou não de cada intervalo.

Tudo bem, essa solução pode te ajudar se quiser seguir nesse rumo, precisará depois pensar numa forma de descobrir qual o valor mínimo e máximo de cada intervalo ? vai, mas dá para seguir por ai sim…

O problema aqui é quando entra na função… Inicialmente, de forma local, o valor de a seria o valor inserido globalmente (supondo que você chame a função com intervalos(a=a) ou somente intervalos(a)). Porém, quando você entra nesses loops o valor dessa variável se tornará o valor de dentro da lista em lista_intervalos, então, a assumirá um valor booleano (True ou False) de acordo com o elemento de lista_intervalos.

Então, vamos simplificar um pouco as coisas. Para checar se o valor faz ou não parte do conjunto, isso ficará fora do loop, já que ele não precisa rodar cada elemento da lista.

Nas seguintes condições, lembre que return irá finalizar a função e tudo vai parar nesse ponto… então, se a = -1, por exemplo, assim que você chamar a função, a primeira coisa que vai acontecer é que o primeiro if receberá um True e então vai executar print('Fora do intervalo') seguido de um return e a função acabará.

Agora, vamos pegar o primeiro laço for de fato, o que vai de 0 a 25:
se a = 5 (do input()), então lista_intervalos será [True, False, False, False], assim, o loop ficará assim por iteração:

1a iteração: if True in range(0,26): → O que retornará um True, já que, para o Python (True == int(1))
2a iteração: if False in range(0,26): → O que retornará um True, já que, para o Python (False == int(0))
3a iteração: if False in range(0,26): → O que retornará um True, já que, para o Python (False == int(0))
4a iteração: if False in range(0,26): → O que retornará um True, já que, para o Python (False == int(0))

Você reparou que a sua verificação deu pau ?

Tenta o seguinte, seguindo ainda a sua ideia de usar uma lista com os intervalos. Define a lista_intervalos de fato com os intervalos:
lista_intervalos = [(0,25), (25,50), (50,75), (75,100)]

ai, dentro do loop, você pode verificar os intervalos 1 a 1:
for intervalo in lista_intervalos:

Assim você tem intervalo[0] como o valor mínimo e intervalo[1] como o valor máximo e pode comparar com intervalo[0] < a <= intervalo[1].

1 curtida

Esses valores booleanos, eu já tinha testado algo parecido hoje cedo, eu só não sabia como fazer ele atribuir os valores aos intervalos da lista. Sabia que tinha algo haver, mas não como usar haha, vou checar aqui.

1 curtida

Reduzir a quantidade de ifs com esse código. O bom desse código é que pode colocar a quantidade de intervalos que quiser.

def esta_no_intervalo(numero: float, intervalo: tuple) -> float:
    if intervalo[0] <= numero <= intervalo[1]:
        print(f'Intervalo [{intervalo[0]},{intervalo[1]}]')
        
        
a = float(input())
lista_de_intervalos = [(0, 25),(25, 50), (50, 75), (75, 100)]

if 0 > a or a > 100:
    print('Fora de intervalo')
else:
    for inter in lista_de_intervalos:
        esta_no_intervalo(a, inter)```
2 curtidas

Gosto de postagens assim, olha só como em um problema simples surgem tantas soluções distintas!

Essa eu considero uma das coisas mais interessantes da programação, existem muitas formas de resolver um mesmo problema.

Se fossemos considerar aqui, neste mesmo problema, o uso de pacotes externos então, ai a coisa ia muito mais longe mesmo!

Além disso, nesse mesmo problema simples pode-se estudar diversas práticas que podem ajudar ou prejudicar aspectos do código como legibilidade, eficiência em uso de memória, eficiência em uso do processamento.

Agora, você conseguiu chegar em uma implementação sua ?