코드의 모든 숫자가 "매직 숫자"로 간주됩니까?


21

그래서 우리가 인수로 메소드에 보내는 코드의 모든 숫자는 매직 넘버로 간주됩니까? 나에게 있어서는 안됩니다. 나는 어떤 숫자가 사용자 길이의 최소 길이라고 말하고 코드에서 "6"을 사용하기 시작한다고 생각합니다 ... 그렇다면 유지 보수 문제가 있고 여기서 "6"은 마법의 숫자입니다 .... 인수 중 하나가 예를 들어 컬렉션의 i 번째 멤버로 정수를 허용하는 메소드를 호출하는 경우 해당 메소드 호출에 "0"을 전달합니다.이 경우에는 "0"을 마술로 볼 수 없습니다. 번호. 어떻게 생각해?


4
귀하의 예에서 0은 무엇을 나타 냅니까?
Aaron Kurtzhals

2
당신이 설명하는 경우에, "0"은 어떤 마법 속성도 갖지 않습니다.
Tulains Córdova

4
0, 1 및 42를 제외한 모든 것은 마법입니다
Mawg

답변:


43

문맥 상 숫자의 의미가 매우 분명 하다면 이것이 "마법의 숫자"문제라고 생각하지 않습니다.

예제 : 처음부터 토큰까지 문자열의 하위 문자열을 가져 오려고하는데 코드가 다음과 같습니다 (상상 언어 및 라이브러리).

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

이러한 맥락에서 숫자 0의 의미는 충분히 명확합니다. 나는 당신이 START_OF_SUBSTRING그것을 정의 하고 0으로 설정할 수 있다고 가정 하지만,이 경우에는 과잉 일 것이라고 생각합니다 (하위 문자열의 시작이 0이 아니라는 것을 알고 있다면 그것은 올바른 접근법 일 것입니다. 당신의 상황).

다른 예는 숫자가 짝수인지 홀수인지 확인하려는 경우 일 수 있습니다. 쓰기:

isEven := x % 2;

다음과 같이 이상하지 않습니다.

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

음수 테스트

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

나에게 이상하다고 느낀다.

isNegativeInt := i <= -1;

6
거기에 또 다른 예를 던지려면 원에서 학위를 사용하여 명시 적으로 작업하는 코드에서 360대부분의 사람들이 그 의미를 알 것이라는 이해로 전체 회전을 표시하는 등의 숫자를 사용하는 것이 좋습니다. 상수를 제공하기 위해 아프지 않은 경우입니다 )
KChaloux

11
KChaloux : 가능하다면, 당신의 의견은 -1입니다. 360은 마법의 숫자입니다. 360이 다른 상수의 값이되면 360과 관련이없고 구분할 수없는 두 세트가 있습니다. 주니어가 와서 "그것은 마법의 숫자입니다", 글로벌 검색으로 이동하고 360을 "Degrees_in_Circle"로 바꾸고 모든 단위 및 회귀 테스트를 실행하십시오. 모든 패스는 코드 수정을 제공합니다. 이제는 개 아침 식사를
코딩

4
@mattnz : 큰 규모의 코드 변경이 프로덕션에 들어가기 오래 전에 빨리 (코드 검토 중에 주니어가되면) 검토 될 수 있기를 바랍니다. 그 맥락에서 그렇게 할 누군가가 아마도 0내 하위 문자열 예제의 맥락에서 대체 될 것이라고 생각 합니다. 이 경우에 발생할 수있는 피해 가 가장 적을 수 있습니다. 기하학적 계산을 수행하는 코딩을 수행 한 지 오랜 시간이 지났지 만 일반적으로 15, 30, 45, 60, 90, 180, 360 값은 상수였습니다. 나는 아무도 정의를 본 적이 없다 FIFTEEN_DEGREES...
FrustratedWithFormsDesigner

5
@KChaloux도에서 라디안으로 이동하면 예제가 실제로 떨어질 수 있습니다. 360으로, 당신은 1 회전을 표현하고 있습니다. 동일한 값에 대해 여러 개의 표현이 있으므로 꺼내야합니다. 특히 360PI가 2PI (180 회전이지만 끝에서 같은 방향을 가리키고 있음)와 같거나 360 회전이 1 회전과 같을 수 있지만 부작용은 다를 수 있습니다.
Chris

14
거기에 약간의 밀짚 남자, TWO와 MINUS_ONE은 마술 숫자를 텍스트로 렌더링하는 것이 OF COURSE 바보이기 때문에 완전히 나쁩니다. 상수의 이름은 그 의미를 전달해야합니다. 귀하의 예가 숫자에 대한 근본적인 사실에 관한 것 외에는 특정 숫자와 밀접한 관련이 있으므로 그 이상의 의미는 없습니다.
Michael Borgwardt

17
bool hasApples = apples > 0;

명백한 영은 부재를 의미합니다. "absenceValue"라는 변수보다 0이 이해하기 쉽습니다.


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

0이 시작 위치라는 것은 명백합니다. "firstPosition"이라는 변수에 혼동 될 것입니다. 그러한 변수는 시작 위치가 변할 수 있는지 궁금하게 만듭니다.


14

무언가를 지속적으로 선언해야하는지 결정할 때 세 가지 핵심 요소를 제안합니다.

  1. 숫자가 정확하고 간결하게 표현 가능한 것입니까?
  2. 값을 변경해야하는 적절한 시나리오가 있지만 코드를 다시 작성할 필요는 없습니다.
  3. 숫자를 보는 사람이 명명 된 상수를 보는 사람보다 더 빠르거나 덜 빨리 인식 할 수 있습니까?

숫자 리터럴은 불필요하게 장황하거나 불필요하게 부정확하거나 둘 다일 수 있으므로 pi와 같은 것은 숫자 리터럴이 아니라 명명 된 상수로 작성해야합니다. 캐시의 슬롯 수와 같은 것은 이름을 사용하는 상수 여야하며 (아래 참고 참조) 캐시를 사용하는 모든 코드를 수정하지 않고도 캐시를 확장 할 수 있습니다. 명령문에서 숫자 "4", "28"및 "29"와 같은 if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28;것은 상수보다 이름을 지정하지 않아야합니다. 표현식은보다 확실히 읽을 수 있기 때문 if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;입니다. 표준 관리자는 해당 연도의 2100 년 2 월 길이가 위의 공식과 일치하지 않음을 나타냅니다. 이러한 날짜를 올바르게 처리하는 데 방해가됩니다 (예 : 정수 오버플로 또는 다른 문제로 코드가 트립되지 않습니다).

규칙 # 2의 중요한 경고는 경우에 따라 코드가 명명 된 상수로 쉽게 표현할 수없는 방식으로 하드 코딩 된 숫자에 의존 할 수 있다는 것입니다. 예를 들어, 이산 파라미터로 전달 된 두 벡터의 교차 곱을 계산하는 방법은 3 차원 벡터에 사용될 때만 의미가 있습니다. 필요한 치수 수는 루틴을 완전히 다시 쓰지 않고 의미있게 변경할 수있는 값이 아닙니다. 3 차원 4 차원 벡터의 교차 곱을 계산할 필요가 있다고하더라도, 값 "3"에 대해 명명 된 상수를 사용하면 그러한 요구를보다 쉽게 ​​충족시킬 수있을 것입니다.


4

이것은 모든 원칙과 마찬가지로 정도의 문제입니다. 일반적으로 소스 코드의 숫자 리터럴은 더 큰 것으로 의심됩니다. 10과 같은 최대 길이 또는 0x587FB0과 같은 메모리 주소는 분명히 나쁜 습관입니다. 조만간이 값을 두 번 이상 반복하여 비 호환성 및 미묘한 오류가 발생할 수있는 위험이 발생할 수 있습니다. 변경되었습니다.

스케일의 다른 끝에 0이 있습니다. 여전히 용의자이지만 그다지 많지는 않습니다. 센티넬 값으로 0을 사용하고 있습니까? 그런 다음 상수가 의미를 설명 할 수 있기 때문에 대신 기호 상수를 사용해야합니다 . "0은 성공적인 완료를 의미합니다"와 같은 매우 문화적인 계약입니까? 아마 괜찮을 것입니다. "컬렉션의 첫 번째 항목"을 의미합니까? 그것은 무해 할 수 있지만, 다른 방법이 있다면 first()선호 할 것입니다.


1
"0을 센티넬 값으로 사용하고 있습니까?" <-여기서 "센티넬"의 의미를 설명 할 수 있습니까? 일치하는 정의를 찾을 수 없습니다.
rory.ap

3

문맥 상 명백하지 않은 이름없는 숫자는 모두 마법의 숫자입니다. 문맥에서 즉시 명백한 의미를 갖는 숫자를 정의하는 것은 조금 바보입니다.

django (python web framework)에서 다음과 같은 원시 숫자로 일부 데이터베이스 필드를 정의 할 수 있습니다.

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

말하는 것보다 더 명확하고 권장되는 방법

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) 

길이를 변경할 필요가 없기 때문에 항상 max_length필드 의 길이와 비교할 수 있습니다 . 응용 프로그램을 처음 배포 한 후 필드 길이를 변경해야하는 경우 django 코드에서 필드 당 정확히 한 위치에서 변경 한 다음 DB의 스키마를 변경하기 위해 마이그레이션을 추가로 작성해야합니다. max_length객체 유형의 정의 된 필드 를 참조해야하는 경우 직접 수행 할 수 있습니다. 해당 필드가 Person클래스 를 정의한 경우 Person._meta.get_field('firstname').max_length에는max_length사용 중 (한 곳에 정의 됨). 여러 필드에 동일한 40이 사용되었다는 사실은 독립적으로 변경하고 싶기 때문에 관련이 없습니다. 이름의 길이는 중간 이름 또는 성의 길이에 의존해서는 안됩니다. 이들은 별도의 값이며 독립적으로 변경 될 수 있습니다.

배열 인덱스는 종종 이름이없는 숫자를 사용할 수 있습니다. 파이썬 사전에 넣을 데이터의 CSV 파일이 있고 행의 첫 번째 요소를 사전으로 key쓰면 다음과 같습니다.

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

물론 나는 이름 index_column = 0을 짓고 다음과 같은 것을 할 수 있습니다.

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

또는 더 나쁜 정의 after_index_col = index_col + 1를 제거하기 위해 정의 index_col+1하지만 내 관점에서 코드가 명확하지는 않습니다. 또한 index_col이름을 지정하면 열이 0이 아니더라도 (그래서 row[:index_col] +부분) 코드를 작동시키는 것이 좋습니다 .


7
실제로 max_lngth=40vs max_length=MAX_LENGTH_NAME는 상징으로 비명지르는 마술 숫자 의 전형적인 예 입니다. 45 개의 문자 이름을 지원하고자하는 날이 올 것입니다. 이제 "40"의 모든 사용이 의심되며 신중하게 검사해야합니다.
로스 패터슨

1
@RossPatterson-이것은 글로벌 var MAX_ARRAY_SIZE와 지속적으로 비교하는 C가 아니지만 적절한 웹 프레임 워크입니다. 매직 넘버가 나오는 유일한 곳은 데이터베이스 모델을 선언하는 곳입니다. 그 밖의 모든 것은이 값과 비교됩니다 (예 : 40은 코드의 다른 곳에 나타나지 않습니다). 또한 DB에 연결된 스키마 마이그레이션을 수행하지 않으면이 변수를 쉽게 변경할 수 없습니다. 나는 코드에서 한 문자 중간 이름에게 변화의 즉시 명백한 한 곳을 말하고 변경을 원하는 경우 401. 맥락을 생각해야합니다.
jimbob 박사

2
죄송합니다. 두 가지 점이 잘못되었습니다. 먼저 OP는 언어를 지정하지 않은 "프로그래밍 사례"질문을했습니다. 그들은 "함수"가 아니라 "방법"이라고 말 했으므로, 객체 지향적 인 것을 가정 해 봅시다. 그러나 그것은 우리를 마법 번호의 데이터 영역에서 빼앗아 가지 않습니다. 둘째, 마법 번호가 데이터베이스 ( 예 : 스키마)에 구워지면 코드에 포함시키는 것이 훨씬 더 나쁩니다. 올바른 방법은 데이터베이스 자체 또는 코드의 수명에 따라 모든 상수를 중앙 집중화하는 스키마 모듈과 같은 소스에서 거의 마법을 얻는 것입니다.
로스 패터슨
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.