Ogni numero nel codice è considerato un "numero magico"?


21

Quindi ogni numero nel codice che stiamo inviando a un metodo come argomento è considerato un numero magico? Per me non dovrebbe. Penso che se un certo numero è, diciamo che è per la lunghezza minima del nome utente e iniziamo a usare "6" nel codice ... quindi sì, abbiamo un problema di manutenzione e qui "6" è un numero magico .... ma se stiamo chiamando un metodo che uno dei suoi argomenti accetta un numero intero, ad esempio come l'ith membro di una raccolta e quindi passiamo "0" a quella chiamata del metodo, in questo caso non vedo che "0" come una magia numero. Cosa pensi?


4
Nel tuo esempio, cosa rappresenta lo 0?
Aaron Kurtzhals l'

2
Nel caso tu illustri, quello "0" non ha proprietà magiche di sorta.
Tulains Córdova,

4
Tutto tranne 0,1 e 42 è magico
Mawg dice di ripristinare Monica il

Risposte:


43

Se il significato del numero è molto chiaro nel contesto, non penso che sia un problema di "numero magico".

Esempio: supponiamo che tu stia cercando di ottenere la sottostringa di una stringa, dall'inizio ad alcuni token e il codice è simile al seguente (linguaggio e libreria immaginari):

s := substring(big_string, 0, findFirstOccurence(SOME_TOKEN, big_string));

In questo contesto, il significato del numero 0 è abbastanza chiaro. Suppongo che potresti definirlo START_OF_SUBSTRINGe impostarlo su 0, ma in questo caso penso che sarebbe eccessivo (anche se sarebbe l'approccio corretto se sapessi che l'inizio della sottostringa potrebbe non essere 0, ma ciò dipende dalle specifiche di la tua situazione).

Un altro esempio potrebbe essere se stai cercando di determinare se un numero è pari o dispari. scrittura:

isEven := x % 2;

non è strano come:

TWO := 2;
isEven := x % TWO;

Test di numeri negativi come

MINUS_ONE := -1;
isNegativeInt := i <= MINUS_ONE;

mi sento anche strano per me, preferirei vedere

isNegativeInt := i <= -1;

6
Per dare un altro esempio, nel codice in cui stai esplicitamente lavorando con gradi in un cerchio, sarebbe giusto usare un numero tale da 360contrassegnare una rotazione completa con la consapevolezza che la maggior parte delle persone saprà cosa significa (anche se questo è un caso in cui non sarebbe male fornire una costante)
KChaloux

11
KChaloux: Se potessi, vorrei -1 il tuo commento. 360 è un numero magico. Se 360 ​​risulta essere un valore per un'altra costante, allora hai 2 set per 360 non correlati e indistinguibili. Arriva Junior, vai "Questo è un numero magico", cerca in tutto il mondo e sostituisci 360 con "Degrees_in_Circle", esegui tutti i test di unità e regressione, tutto passa - fornisce correzione del codice. Codifica ora una colazione per cani, e sappiamo tutti cosa succede dopo poco .......
mattnz

4
@mattnz: Spero che quel tipo di modifica del codice su larga scala venga catturato rapidamente (si spera durante la revisione del codice, se sono così piccoli) molto prima che entrasse in produzione. Penso che qualcuno che lo farebbe in quel contesto probabilmente sostituirebbe anche 0nel contesto del mio esempio di sottostringa. In tal caso, questa potrebbe essere la minima quantità di danno che possono causare. È passato molto tempo da quando ho fatto una codifica che eseguiva calcoli geometrici, ma in genere i valori 15, 30, 45, 60, 90, 180, 360 erano costanti accettate. Non ho mai visto nessuno definire FIFTEEN_DEGREES...
FrustratedWithFormsDesigner

5
@KChaloux L'esempio potrebbe effettivamente andare in pezzi se c'è un passaggio da gradi a radianti. Di 360, stai esprimendo 1 rotazione completa. Poiché esistono più rappresentazioni per lo stesso valore, è necessario estrarle. Soprattutto se si considera che 360PI potrebbe avere lo stesso aspetto di 2PI (180 rotazioni ma che punta comunque alla stessa direzione alla fine), o 360 rotazioni uguali a 1 rotazione, ma gli effetti collaterali possono essere diversi.
Chris

14
Un po 'un uomo di paglia lì, DUE e MINUS_ONE sono assolutamente cattivi perché sostituire un numero magico con il suo rendering nel testo è DIFFICILE idiota. Il nome della costante deve trasmettere il suo significato. Tranne il fatto che i tuoi esempi riguardano fatti fondamentali sui numeri, strettamente legati solo a quei numeri specifici, quindi non c'è davvero alcun significato oltre a quello.
Michael Borgwardt,

17
bool hasApples = apples > 0;

È ovvio zero significa assenza. Trovo 0 più facile da capire di una variabile denominata "encyValue ".


for(int i=0; i < arr.length; i++)

È ovvio che 0 è la posizione iniziale. Sarei confuso da una variabile denominata "firstPosition". Una tale variabile mi farebbe meravigliare se la posizione iniziale potesse cambiare.


14

Vorrei suggerire tre fattori chiave per decidere se qualcosa dovrebbe essere una dichiarazione costante:

  1. Il numero è qualcosa che è rappresentabile in modo preciso e conciso
  2. Esistono scenari plausibili in cui il valore dovrebbe cambiare, ma il codice non dovrebbe essere riscritto
  3. Qualcuno che vede il numero sarebbe in grado di riconoscerlo più rapidamente o meno rapidamente di qualcuno che vede una costante nominata

Qualcosa come pi dovrebbe probabilmente essere scritto come costante nominata, piuttosto che come un letterale numerico, poiché un letterale numerico può essere inutilmente prolisso, inutilmente impreciso o entrambi. Qualcosa di simile al numero di slot in una cache dovrebbe essere probabilmente una costante con nome (anche se vedi la nota sotto) per consentire la possibilità di espandere la cache senza dover modificare tutto il codice che la utilizza. Cose come i numeri "4", "28" e "29" nell'istruzione if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28;probabilmente non dovrebbero essere chiamati costanti, poiché l'espressione è quasi certamente più leggibile di if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;. Tieni presente che i manutentori degli standard hanno indicato che la lunghezza di febbraio del 2100 in quell'anno non corrisponderà alla formula di cui sopra, impedimento alla corretta gestione di tali date (ovvero il codice non verrà attivato da overflow di numeri interi o altri problemi simili).

Un avvertimento importante con la regola n. 2 è che in alcuni casi il codice può fare affidamento su numeri hardcoded in un modo che non può essere prontamente rappresentato da una costante denominata. Ad esempio, un metodo che calcola un prodotto incrociato di due vettori passati come parametri discreti sarà significativo solo se utilizzato su vettori tridimensionali. Il numero richiesto di dimensioni non è un valore che potrebbe essere modificato in modo significativo senza riscrivere completamente la routine. Anche se si prevedeva una possibile necessità di calcolare il prodotto incrociato di tre vettori a 4 dimensioni, l'uso di una costante denominata per il valore "3" farebbe ben poco per rendere più semplice soddisfare tale esigenza.


4

Questo, come tutti i principi, è una questione di laurea. In generale, i letterali numerici nel codice sorgente sono più sospetti quanto più sono grandi. Una lunghezza massima come 10 o un indirizzo di memoria come 0x587FB0 sono ovviamente cattive pratiche - è quasi certo che prima o poi dovrai ripetere questi valori più di una volta, creando un rischio di incompatibilità e sottili errori introdotti in luoghi che non erano cambiato.

0 è all'altra estremità della scala; è ancora sospetto ma non altrettanto. Stai usando 0 come valore sentinella? Quindi dovresti probabilmente usare una costante simbolica, solo perché la costante può spiegare cosa significa. È un accordo culturale estremamente radicato come "0 significa completamento riuscito"? Questo è probabilmente OK. Significa "il primo oggetto in una collezione"? Potrebbe essere innocuo, ma se esiste un metodo alternativo come first()probabilmente lo preferirei.


1
"Stai usando 0 come valore sentinella?" <- Puoi spiegare cosa intendi per "sentinella" qui? Non riesco a trovare una definizione che sembra corrispondere.
rory.ap,

3

Ogni numero senza nome che non è immediatamente evidente dal contesto è un numero magico. È un po 'sciocco definire numeri che abbiano un significato che è immediatamente evidente dal contesto.

In django (python web framework), posso definire alcuni campi del database con un numero non elaborato come:

firstname = models.CharField(max_length=40)
middlename = models.CharField(max_length=40)
lastname =  models.CharField(max_length=40) 

che è più chiaro (e la pratica consigliata ) di dire

MAX_LENGTH_NAME = 40
...
firstname = models.CharField(max_length=MAX_LENGTH_NAME)
middlename = models.CharField(max_length=MAX_LENGTH_NAME)
lastname =  models.CharField(max_length=MAX_LENGTH_NAME) 

poiché è improbabile che abbia mai bisogno di cambiare la lunghezza (e può sempre confrontare con max_lengthil campo). Se devo modificare la lunghezza del campo dopo aver inizialmente distribuito l'applicazione, devo cambiarlo esattamente in una posizione per campo nel mio codice django, quindi scrivere una migrazione per cambiare lo schema del DB. Se mai dovessi fare riferimento a max_lengthun campo definito di un tipo di oggetto, posso farlo direttamente - se quei campi stavano definendo una Personclasse, posso usare Person._meta.get_field('firstname').max_lengthper ottenere ilmax_lengthin uso (che è definito in un unico posto). Il fatto che lo stesso 40 sia stato utilizzato per più campi è irrilevante in quanto potrei volerli modificare in modo indipendente. La lunghezza del nome non dovrebbe mai dipendere dalla lunghezza del nome medio o del cognome; sono valori separati e possono cambiare indipendentemente.

Spesso gli indici di array possono usare numeri senza nome; come se avessi un file CSV di dati che volevo mettere in un dizionario Python, con il primo elemento nella riga come il dizionario keyche scriverei:

mydict = {}
for row in csv.reader(f):
    mydict[row[0]] = row[1:]

Sicuro che potrei nominare index_column = 0e fare qualcosa del tipo:

index_col = 0
mydict = {}
for row in csv.reader(f):
    mydict[row[index_col]] = row[:index_col] + row[index_col+1:]

o peggio definire after_index_col = index_col + 1per sbarazzarsi di index_col+1, ma ciò non rende il codice più chiaro a mio avviso. Inoltre, se do index_colun nome, farò meglio a far funzionare il codice anche se la colonna non è 0 (quindi la row[:index_col] +parte).


7
In realtà, max_lngth=40vs. max_length=MAX_LENGTH_NAMEè un classico esempio di un numero magico che grida di essere un simbolo. Verrà il giorno in cui vuoi supportare 45 nomi di personaggi, e ora ogni uso di "40" è sospetto e deve essere attentamente esaminato.
Ross Patterson,

1
@RossPatterson - Questo non è C dove confrontiamo costantemente un MAX_ARRAY_SIZE var globale, ma un framework web decente. L'unico posto in cui compare il numero magico è dove si dichiara il modello di database; tutto il resto viene confrontato con questo valore (ad esempio, 40 non appare in nessun altro punto del codice). Inoltre, non è possibile modificare facilmente questa variabile senza eseguire migrazioni di schemi in quanto legate a un DB. Se volessi cambiare dire 1 carattere nomi di mezzo il suo immediatamente evidente l'unico posto al cambiamento nel codice 40a 1. Devi pensare al contesto.
dr jimbob,

2
Scusa, ti sbagli su due punti. Innanzitutto, l'OP ha posto una domanda "pratiche di programmazione" che non specifica alcuna lingua. Hanno detto "metodo", non "funzione", quindi supponiamo qualcosa orientato agli oggetti, ma ciò non ci porta fuori dal regno dei dati numerati magicamente. In secondo luogo, se il numero magico viene inserito nel database ( ad esempio , lo schema), è ancora peggio averlo nel codice. La cosa giusta da fare è ottenere la magia quasi una costante dalla sua fonte - o il database stesso o un modulo di schema che centralizza tutte quelle costanti che varieranno nel corso della vita del codice.
Ross Patterson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.