4
votes

Méthode efficace Python pour écrire un cas de commutateur avec comparaison

J'implémente normalement Switch / Case pour une comparaison égale en utilisant Dictionary.

{[10]:'A', [10,30]:'B', [30,50]:'C',[50,90]:'D',[90]:'E'}

au lieu de

if score <=10 :
  cat = 'A'
elif score >10 and score <=30:
  cat = 'B'
elif score >30 and score <=50 :
  cat = 'C'
elif score >50 and score <=90 :
  cat = 'D'
else:
  cat = 'E'

Existe-t-il une stratégie pour mettre en œuvre une stratégie approche pour une comparaison non-égale?

if a==0 :
  res = 'zero'
elif a == 1:
  res = 'one'
elif a==2:
  res = 'two'

Je sais que cela peut être délicat avec ,> =, mais existe-t-il une stratégie pour généraliser ou générer instructions automatiques de disons une liste

dict = {0:'zero', 1:'one', 2:'two'}; 
a=1; res = dict[a]

et un drapeau pour dire si c'est


3 commentaires

Nous devons sûrement avoir un double pour ce genre de questions maintenant? Quelqu'un?


Python n'a pas d'instruction switch. Utilisez juste if, elif, else


Argh, bien sûr, cette question ici est également inadaptée pour être un bon canonique ... il suffit avait de demander un moyen de distinguer


5 Réponses :


3
votes

Le module bisect peut être utilisé pour un tel problème de catégorisation. En particulier, la documentation propose un exemple qui résout un problème très similaire

Voici le même exemple adapté à votre cas d'utilisation. La fonction renvoie deux valeurs: la note de la lettre et un indicateur bool qui indique si la correspondance était exacte.

from bisect import bisect_left

grades = "ABCDE"
breakpoints = [10, 30, 50, 90, 100]

def grade(score):
          index = bisect_left(breakpoints, score)
          exact = score == breakpoints[index]
          grade = grades[index]
          return grade, exact

grade(10) # 'A', True
grade(15) # 'B', False

Dans ce qui précède, j'ai supposé que votre dernier point d'arrêt était 100 pour E . Si vous ne voulez vraiment pas de limite supérieure, notez que vous pouvez remplacer 100 par math.inf pour que le code fonctionne.


0 commentaires

1
votes

Oui, il existe une stratégie, mais pas aussi claire que les schémas de pensée humains. Tout d'abord quelques notes:

  • Il y a d'autres questions concernant le "commutateur Python"; Je suppose que vous les avez déjà consultés et que vous n'avez plus envisagé ces solutions.
  • La structure que vous avez publiée n'est pas une liste ; c'est une tentative invalide d'un dict . Les clés doivent être hachables; les listes que vous donnez ne sont pas des clés valides.
  • Vous avez deux types de comparaison distincts ici: la correspondance exacte avec la limite inférieure et le confinement de la plage.

Cela dit, je conserverai le concept de table de recherche, mais nous allons le ramener à un faible dénominateur commun pour le rendre facile à comprendre et à modifier pour d'autres considérations.

low = [10, 30, 50, 90]
grade = "ABCDE"

for idx, bkpt in enumerate(low):
    if score <= bkpt:
        exact = (score == bkpt)
        break

cat = grade[idx]

exact est l'indicateur que vous avez demandé.


0 commentaires

1
votes

Pour votre cas particulier, une approche efficace pour convertir un score en une note en complexité temporelle O (1) serait d'utiliser 100 moins le score divisé par 10 comme index de chaîne pour obtenir la lettre note:

E
E
D
C
B
A
A

pour que:

print(get_grade(100))
print(get_grade(91))
print(get_grade(90))
print(get_grade(50))
print(get_grade(30))
print(get_grade(10))
print(get_grade(0))

affiche:

def get_grade(score):
    return 'EDDDDCCBBAA'[(100 - score) // 10]


0 commentaires

4
votes

Un dictionnaire peut contenir beaucoup de valeurs, si vos plages ne sont pas trop larges, vous pouvez créer un dictionnaire similaire à celui que vous aviez pour les conditions d'égalité en développant chaque plage par programme:

def rangeMap(*breaks,inclusive=False):
    default = breaks[-1] if len(breaks)&1 else None
    breaks  = list(zip(breaks[::2],breaks[1::2]))
    def mapValueLT(value):
        return next( (tag for tag,bound in breaks if value<bound), default)
    def mapValueLE(value):
        return next( (tag for tag,bound in breaks if value<=bound), default)
    return mapValueLE if inclusive else mapValueLT

scoreToCategory = rangeMap('A',10,'B',30,'C',50,'D',90,'E')

print(scoreToCategory(53)) # D
print(scoreToCategory(30)) # C

scoreToCategoryLE = rangeMap('A',10,'B',30,'C',50,'D',90,'E',inclusive=True)

print(scoreToCategoryLE(30)) # B

Ce dernier peut également être exprimé à l'aide d'une compréhension de liste et d'un tableau de correspondance:

score = 43
case = lambda x: score < x
if   case(10): cat = "A"
elif case(30): cat = "B"
elif case(50): cat = "C"
elif case(90): cat = "D"
else         : cat = "E"
print (cat) # 'C'

[EDIT] plus spécifiquement à la question, une solution généralisée peut être créée à l'aide d'une fonction de configuration qui renvoie une fonction de mappage en fonction de vos paramètres:

if   score < 10 : cat = 'A'
elif score < 30 : cat = 'B'
elif score < 50 : cat = 'C'
elif score < 90 : cat = 'D'
else            : cat = 'E'

notez qu'avec un peu plus de travail vous pouvez améliorer le performance de la fonction retournée en utilisant le module bisect


0 commentaires

-1
votes
low = [10,30,50,70,90]
gradE = "FEDCBA"

def grade(score):
    for i,b in enumerate(low):
        #if score < b:   # 0--9F,10-29E,30-49D,50-69C,70-89B,90-100A Easy
        if score <= b:   # 0-10F,11-30E,31-50D,51-70C,71-90B,91-100A Taff
            return gradE[i]
    else:return gradE[-1]

for score in range(0,101):
    print(score,grade(score))

0 commentaires