Czy każda liczba w kodzie jest uważana za „magiczną liczbę”?

21

Więc każda liczba w kodzie, który wysyłamy do metody jako argument, jest uważana za liczbę magiczną? Dla mnie nie powinno. Myślę, że jeśli jest jakaś liczba, powiedzmy, że jest to minimalna długość nazwy użytkownika i zaczynamy używać „6” w kodzie ... to tak, mamy problem z konserwacją, a tutaj „6” to magiczna liczba… ale jeśli wywołujemy metodę, w której jeden z jej argumentów przyjmuje liczbę całkowitą, na przykład jako ity element kolekcji, a następnie przekazujemy „0” do tego wywołania metody, w tym przypadku nie widzę tego „0” jako magii numer. Co myślisz?

Blake
źródło
4
W twoim przykładzie, co oznacza 0?
Aaron Kurtzhals
2
Jeśli zilustrujesz to, że „0” nie ma żadnych magicznych właściwości.
Tulains Córdova
4
Wszystko oprócz 0,1 i 42 jest magiczne
Mawg

Odpowiedzi:

43

Jeśli znaczenie liczby jest bardzo jasne w kontekście, nie sądzę, że jest to problem z „magiczną liczbą”.

Przykład: Załóżmy, że próbujesz pobrać podciąg łańcucha od początku do jakiegoś tokena, a kod wygląda następująco (wymyślony język i biblioteka):

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

W tym kontekście znaczenie liczby 0 jest wystarczająco jasne. Przypuszczam, że możesz zdefiniować START_OF_SUBSTRINGi ustawić na 0, ale w tym przypadku myślę, że byłoby to przesadą (chociaż byłoby to właściwe podejście, gdybyś wiedział, że początek twojego podłańcucha może nie być 0, ale to zależy od specyfiki Twoja sytuacja).

Innym przykładem może być próba ustalenia, czy liczba jest parzysta czy nieparzysta. Pisanie:

isEven := x % 2;

nie jest tak dziwny jak:

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

Testowanie liczb ujemnych jako

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

też wydaje mi się dziwne, wolałbym raczej zobaczyć

isNegativeInt := i <= -1;
FrustratedWithFormsDesigner
źródło
6
Aby rzucić na to kolejny przykład, w kodzie, w którym wyraźnie pracujesz ze stopniami na kole, byłoby sprawiedliwe, aby użyć liczby takiej jak 360zaznaczenie pełnego obrotu ze zrozumieniem, że większość ludzi będzie wiedziała, co to znaczy (chociaż to to przypadek, w którym nie zaszkodzi zapewnić stałą)
KChaloux
11
KChaloux: Gdybym mógł, zrobiłbym -1 twój komentarz. 360 to magiczna liczba. Jeśli 360 jest wartością innej stałej, masz 2 zestawy dla 360, które są niepowiązane i nierozróżnialne. Nadchodzi Junior, idzie „To magiczna liczba”, globalne wyszukiwanie i zamienia 360 na „Degrees_in_Circle”, uruchamia wszystkie testy jednostkowe i regresyjne, wszystko przechodzi - poprawia kod. Koduj teraz śniadanie dla psów, a wszyscy wiemy, co się z tym stanie po krótkim czasie .......
mattnz
4
@mattnz: Mam nadzieję, że taka zmiana kodu na dużą skalę zostanie szybko uchwycona (mam nadzieję, że podczas przeglądu kodu, jeśli są one młodsze) na długo przed wprowadzeniem go do produkcji. Myślę, że ktoś, kto zrobiłby to w tym kontekście, prawdopodobnie zastąpiłby również 0w kontekście mojego przykładu podłańcucha. W takim przypadku może to być najmniejsza ilość szkód, jakie mogą spowodować. Minęło dużo czasu, odkąd zrobiłem kodowanie, które wykonało obliczenia geometryczne, ale ogólnie wartości 15, 30, 45, 60, 90, 180, 360 były stałymi, które zostały zaakceptowane. Nigdy nie widziałem, żeby ktoś zdefiniował FIFTEEN_DEGREES...
FrustratedWithFormsDesigner
5
@KChaloux Przykład może się rozpaść, jeśli nastąpi przejście ze stopni na radianów. Przez 360 wyrażasz 1 pełny obrót. Ponieważ istnieje wiele reprezentacji dla tej samej wartości, należy ją wyciągnąć. Zwłaszcza biorąc pod uwagę, że 360PI może wyglądać tak samo jak 2PI (180 obrotów, ale wciąż wskazuje ten sam kierunek na końcu) lub 360 obrotów tak samo jak 1 obrót, ale efekty uboczne mogą być różne.
Chris
14
Trochę słaby człowiek, TWO i MINUS_ONE są całkowicie źli, ponieważ zastąpienie magicznej liczby jej renderowaniem w tekście jest oczywiście OCZYWIŚCIE idiotyczne. Nazwa stałej musi przekazywać swoje znaczenie. Tyle że twoje przykłady dotyczą podstawowych faktów na temat liczb, ściśle powiązanych tylko z tymi konkretnymi liczbami, więc tak naprawdę nie ma poza tym żadnego znaczenia.
Michael Borgwardt,
17
bool hasApples = apples > 0;

To oczywiste, że zero oznacza brak. Uważam, że 0 jest łatwiejsze do zrozumienia niż zmienna o nazwie „failedValue”.


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

To oczywiste, że 0 to pozycja początkowa. Byłbym zdezorientowany przez zmienną o nazwie „firstPosition”. Taka zmienna sprawiłaby, że zastanawiałem się, czy pozycja początkowa mogłaby się zmienić.

mike30
źródło
14

Sugerowałbym trzy kluczowe czynniki przy podejmowaniu decyzji, czy coś powinno być stałą deklaracją:

  1. Jest liczbą, która jest precyzyjnie i zwięźle reprezentowalna
  2. Czy istnieją jakieś prawdopodobne scenariusze, w których wartość musiałaby się zmienić, ale kod nie musiałby zostać przepisany
  3. Czy ktoś, kto zobaczy numer, będzie w stanie rozpoznać go szybciej lub wolniej niż ktoś, kto widzi nazwaną stałą

Coś w rodzaju pi powinno prawdopodobnie być zapisane jako nazwana stała, a nie jako literał liczbowy, ponieważ literał liczbowy może być niepotrzebnie gadatliwy, niepotrzebnie nieprecyzyjny lub jedno i drugie. Coś w rodzaju liczby miejsc w pamięci podręcznej powinno być nazwaną stałą (choć patrz uwaga poniżej), aby umożliwić możliwość rozszerzenia pamięci podręcznej bez konieczności modyfikowania całego kodu, który z niej korzysta. Rzeczy takie jak cyfry „4”, „28” i „29” w instrukcji if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28;prawdopodobnie nie powinny być nazwane stałymi, ponieważ wyrażenie jest prawie na pewno bardziej czytelne niż if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;. Pamiętaj, że osoby przestrzegające standardów wskazały, że długość 2100 lutego w tym roku nie będzie zgodna z powyższą formułą, przeszkoda w prawidłowej obsłudze takich dat (tzn. kod nie zostanie wyzwolony przez przepełnienie liczb całkowitych lub inne podobne problemy).

Ważnym zastrzeżeniem związanym z zasadą nr 2 jest to, że w niektórych przypadkach kod może polegać na liczbach zakodowanych na stałe w sposób, którego nie można łatwo przedstawić za pomocą nazwanej stałej. Na przykład metoda, która oblicza iloczyn krzyżowy dwóch wektorów przekazanych jako parametry dyskretne, będzie miała znaczenie tylko w przypadku zastosowania w wektorach trójwymiarowych. Wymagana liczba wymiarów nie jest wartością, którą można znacznie zmienić bez całkowitego przepisania procedury. Nawet gdyby przewidzieć możliwą potrzebę obliczenia iloczynu krzyżowego trzech 4-wymiarowych wektorów, użycie nazwanej stałej dla wartości „3” niewiele by zrobiło, aby ułatwić zaspokojenie tej potrzeby.

supercat
źródło
4

To, jak wszystkie zasady, jest kwestią stopnia. Ogólnie rzecz biorąc, literały liczbowe w kodzie źródłowym są bardziej podejrzane, im są większe. Maksymalna długość jak 10 lub adres pamięci jak 0x587FB0 to oczywiście zła praktyka - jest prawie pewne, że prędzej czy później będziesz musiał powtórzyć te wartości więcej niż jeden raz, stwarzając ryzyko niezgodności i subtelnych błędów wprowadzanych w miejscach, które nie były zmienione.

0 znajduje się na drugim końcu skali; wciąż jest podejrzane, ale nie tak bardzo. Czy używasz 0 jako wartości wartownika? Wtedy prawdopodobnie powinieneś użyć stałej symbolicznej, tylko dlatego, że stała może wyjaśnić, co to znaczy. Czy jest to wyjątkowo zakorzenione porozumienie kulturowe, takie jak „0 oznacza pomyślne zakończenie”? To chyba OK. Czy to oznacza „pierwszy element w kolekcji”? Może to być nieszkodliwe, ale jeśli istnieje alternatywna metoda, taka first(), którą prawdopodobnie wolałbym.

Kilian Foth
źródło
1
„Czy używasz 0 jako wartości wartownika?” <- Czy możesz tutaj wyjaśnić, co rozumiesz przez „wartownik”? Nie mogę znaleźć definicji, która wydaje się pasować.
rory.ap
3

Każda nienazwana liczba, która nie jest od razu oczywista z kontekstu, jest liczbą magiczną. Trochę głupio jest definiować liczby, które mają znaczenie, które jest natychmiast oczywiste z kontekstu.

W django (frameworku Python) mogę zdefiniować pole bazy danych o nieprzetworzonej liczbie, takie jak:

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

co jest jaśniejsze (i zalecana praktyka ) niż powiedzieć

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) 

ponieważ prawdopodobnie nigdy nie będę musiał zmieniać długości (i zawsze mogę to porównać do max_lengthpola). Jeśli muszę zmienić długość pola po początkowym wdrożeniu aplikacji, muszę zmienić go dokładnie w jednym miejscu na pole w moim kodzie django, a następnie dodatkowo napisać migrację, aby zmienić schemat bazy danych. Jeśli kiedykolwiek będę potrzebować odwoływać się max_lengthdo zdefiniowanego pola typu obiektu, mogę to zrobić bezpośrednio - jeśli pola te definiują Personklasę, mogę użyć, Person._meta.get_field('firstname').max_lengthaby uzyskaćmax_lengthw użyciu (który jest zdefiniowany w jednym miejscu). Fakt, że tych samych 40 użyto do wielu pól, nie ma znaczenia, ponieważ mogę chcieć je zmienić niezależnie. Długość imienia nigdy nie powinna zależeć od długości imienia i nazwiska; są to osobne wartości i mogą się zmieniać niezależnie.

Indeksy tablicowe często mogą używać liczb nienazwanych; na przykład, jeśli mam plik CSV danych, który chcę umieścić w słowniku Pythona, z pierwszym elementem w wierszu jako słownik key, który napisałbym:

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

Pewnie, że mógłbym nazwać index_column = 0i zrobić coś takiego:

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

lub gorzej zdefiniować, after_index_col = index_col + 1aby się go pozbyć index_col+1, ale to nie sprawia, że ​​kod jest jaśniejszy. Ponadto, jeśli podam index_colnazwę, lepiej sprawię, aby kod działał, nawet jeśli kolumna nie jest równa 0 (stąd row[:index_col] +część).

dr jimbob
źródło
7
W rzeczywistości max_lngth=40vs. max_length=MAX_LENGTH_NAMEto klasyczny przykład magicznej liczby, która krzyczy, że jest symbolem. Nadejdzie dzień, w którym chcesz wesprzeć 45 nazw postaci, a teraz każde użycie „40” jest podejrzane i musi zostać dokładnie zbadane.
Ross Patterson
1
@RossPatterson - To nie jest C, gdzie stale porównujemy z globalnym var MAX_ARRAY_SIZE, ale przyzwoita platforma internetowa. Jedynym miejscem, w którym pojawia się magiczna liczba, jest miejsce, w którym deklarujesz model bazy danych; wszystko inne jest porównywane z tą wartością (np. 40 nie pojawia się nigdzie indziej w kodzie). Należy również pamiętać, że nie można łatwo zmienić tej zmiennej bez wykonywania migracji schematu, ponieważ jest ona powiązana z bazą danych. Gdybym chciał zmienić powiedzieć 1 znak drugie imię jej natychmiast oczywiste, że jedno miejsce do zmian w kodzie 40do 1. Musisz pomyśleć o kontekście.
dr jimbob
2
Przepraszamy, mylisz się co do dwóch punktów. Po pierwsze, OP zadał pytanie „praktyk programowania”, które nie określa żadnego języka. Mówili „metoda”, a nie „funkcja”, więc załóżmy, że jest zorientowana obiektowo, ale to nie wyciąga nas z królestwa danych numerowanych magicznie. Po drugie, jeśli magiczna liczba jest zapisana w bazie danych ( np . Schemat), to jeszcze gorzej jest mieć ją w kodzie. Właściwe jest, aby uzyskać magię prawie stałą ze źródła - albo z samej bazy danych, albo z modułu schematu, który scentralizuje wszystkie te stałe, które będą się zmieniać w całym okresie istnienia kodu.
Ross Patterson