2009-07-29 6 views
11

Sto refactoring una funzione che, data una serie di endpoint che definiscono implicitamente gli intervalli, controlla se un numero è incluso nell'intervallo e quindi restituisce un corrispondente (non correlato in alcun modo calcolabile). Il codice che ora gestisce il lavoro è:Python: mappatura da intervalli a valori

if p <= 100: 
    return 0 
elif p > 100 and p <= 300: 
    return 1 
elif p > 300 and p <= 500: 
    return 2 
elif p > 500 and p <= 800: 
    return 3 
elif p > 800 and p <= 1000: 
    return 4 
elif p > 1000: 
    return 5 

Che è IMO abbastanza orribile, e manca in quanto entrambi gli intervalli ed i valori di ritorno sono hardcoded. Qualsiasi utilizzo di qualsiasi struttura dati è ovviamente possibile.

risposta

35
import bisect 
bisect.bisect_left([100,300,500,800,1000], p) 
+0

+1 Mi piace. Si impara qualcosa di nuovo ogni giorno. – kjfletch

+0

+1: incredibile! –

+1

Davvero impressionante. Super pulito, e credo anche molto veloce. Può anche essere facilmente esteso nel caso in cui uno abbia bisogno di un ordinamento non naturale o qualcos'altro in cambio, come una stringa: importazione bisect n = bisect.bisect_left ([100,300,500,800,1000], p) a = ["assente", "basso", "medio", "alto", "molto alto", "estremo"] a [n] – Agos

0

provare qualcosa sulla falsariga di:

d = {(None,100): 0, 
    (100,200): 1, 
    ... 
    (1000, None): 5} 
value = 300 # example value 
for k,v in d.items(): 
    if (k[0] is None or value > k[0]) and (k[1] is None or value <= k[1]): 
     return v 
3

Si potrebbe provare un prendere su questo:

def check_mapping(p): 
    mapping = [(100, 0), (300, 1), (500, 2)] # Add all your values and returns here 

    for check, value in mapping: 
     if p <= check: 
      return value 

print check_mapping(12) 
print check_mapping(101) 
print check_mapping(303) 

produce:

0 
1 
2 

Come sempre in Python, non ci sarà qualche modo migliore per farlo.

+0

Non considera il caso di p> 1000! – stefanw

+0

Questo è il motivo per cui ho specificato: "Potresti provare un modo per farlo" – kjfletch

+0

L'ultima frase è ironica, considerando la filosofia di Python di avere preferibilmente un solo modo ovvio per fare qualcosa. – sykora

0
def which_interval(endpoints, number): 
    for n, endpoint in enumerate(endpoints): 
     if number <= endpoint: 
      return n 
     previous = endpoint 
    return n + 1 

passare il vostro endpoint come un elenco in endpoints, in questo modo:

which_interval([100, 300, 500, 800, 1000], 5) 

Edit:

Quanto sopra è una ricerca lineare. La risposta di Glenn Maynard avrà prestazioni migliori, poiché utilizza un algoritmo di bisezione.

+0

Perdere il caper "precedente"; è abbastanza ridondante. –

+0

Sì, hai ragione, immagino che il codice originale "mi abbia ispirato" a usarlo. A proposito, l'uso dell'imperativo potrebbe sembrare un po 'burbero per alcuni. – Steef

+0

@Steef: Potresti considerare un umile suggerimento che potresti rivedere la tua risposta a piacere, nota che ** la tua risposta include ancora una riga di codice ridondante **, e nella pienezza dei tempi, accise lo stesso. –

0

Un altro modo ...

def which(lst, p): 
    return len([1 for el in lst if p > el]) 

lst = [100, 300, 500, 800, 1000] 
which(lst, 2) 
which(lst, 101) 
which(lst, 1001) 
3

E 'infatti abbastanza orribile. Senza l'obbligo di non hanno hardcoding, essa avrebbe dovuto essere scritta così:

if p <= 100: 
    return 0 
elif p <= 300: 
    return 1 
elif p <= 500: 
    return 2 
elif p <= 800: 
    return 3 
elif p <= 1000: 
    return 4 
else: 
    return 5 

Ecco alcuni esempi di creazione di una funzione di ricerca, sia lineari che usando la ricerca binaria, con l'obbligo di non-hardcodings soddisfatte, e un paio di controlli di sanità sulle due tabelle:

def make_linear_lookup(keys, values): 
    assert sorted(keys) == keys 
    assert len(values) == len(keys) + 1 
    def f(query): 
     return values[sum(1 for key in keys if query > key)] 
    return f 

import bisect 
def make_bisect_lookup(keys, values): 
    assert sorted(keys) == keys 
    assert len(values) == len(keys) + 1 
    def f(query): 
     return values[bisect.bisect_left(keys, query)] 
    return f 
+0

Mi piace questo meglio rispetto a quello che ha più voti a causa della sua forma più generalizzata/non codificata e perché è più approfondito. – JAB