코드의 모든 숫자가 “매직 숫자”로 간주됩니까? 코드의 모든 숫자는 매직 넘버로 간주됩니까? 나에게

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



답변

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

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

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;

답변

bool hasApples = apples > 0;

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

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

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


답변

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

  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”에 대해 명명 된 상수를 사용하면 그러한 요구를보다 쉽게 ​​충족시킬 수있을 것입니다.


답변

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

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


답변

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

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] +부분) 코드를 작동시키는 것이 좋습니다 .