ทุกหมายเลขในรหัสถือเป็น“ หมายเลขวิเศษ” หรือไม่


21

ดังนั้นทุกหมายเลขในรหัสที่เราส่งไปยังเมธอดในขณะที่อาร์กิวเมนต์ถือเป็น Magic Number? สำหรับฉันมันไม่ควร ฉันคิดว่าถ้ามีบางหมายเลขสมมติว่ามันเป็นความยาวขั้นต่ำของชื่อผู้ใช้และเราเริ่มใช้ "6" ในรหัส ... ใช่แล้วเรามีปัญหาการบำรุงรักษาและที่นี่ "6" เป็นหมายเลขมายากล .... แต่ ถ้าเรากำลังเรียกใช้เมธอดที่หนึ่งในข้อโต้แย้งยอมรับจำนวนเต็มเช่นสมาชิก ith ของคอลเลกชันและจากนั้นเราจะส่ง "0" ไปยังการเรียกเมธอดนั้นในกรณีนี้ฉันไม่เห็นว่า "0" เป็นเวทย์มนตร์ จำนวน. คุณคิดอย่างไร?


4
ในตัวอย่างของคุณ 0 สิ่งที่เป็นตัวแทน?
แอรอน 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 แต่ในกรณีนี้ฉันคิดว่ามันจะ overkill (แม้ว่าจะเป็นวิธีที่ถูกต้องหากคุณรู้ว่าการเริ่มต้นของสตริงย่อยของคุณอาจไม่ใช่ 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 เป็นค่าสำหรับค่าคงที่อื่นคุณมี 2 ชุดสำหรับ 360 ที่ไม่เกี่ยวข้องและไม่สามารถแยกออกได้ จูเนียร์มาพร้อมไป "นั่นคือหมายเลขเวทย์มนตร์" ค้นหาทั่วโลกและแทนที่ 360 ด้วย "Degrees_in_Circle" เรียกใช้การทดสอบหน่วยและการถดถอยทั้งหมดผ่านทั้งหมด - มอบการแก้ไขรหัส รหัสตอนนี้เป็นอาหารเช้าสุนัขและเราทุกคนรู้ว่าเกิดอะไรขึ้นหลังจากนั้นไม่นาน .......
mattnz

4
@ แมทซ์: หวังว่าการเปลี่ยนแปลงรหัสขนาดใหญ่จะถูกจับได้อย่างรวดเร็ว (หวังว่าในระหว่างการตรวจสอบโค้ดหากพวกเขาอยู่ในรุ่นจูเนียร์) นานก่อนที่มันจะได้มีการผลิต ฉันคิดว่าคนที่จะทำเช่นนั้นในบริบทนั้นอาจแทนที่ด้วย0ในบริบทของตัวอย่าง substring ของฉัน ในกรณีนี้นี่อาจเป็นจำนวนน้อยที่สุดของความเสียหายที่พวกเขาสามารถทำให้เกิด เป็นเวลานานแล้วที่ฉันได้ทำการเข้ารหัสที่คำนวณทางเรขาคณิต แต่โดยทั่วไปแล้วค่า 15, 30, 45, 60, 90, 180, 360, 360 เป็นค่าคงที่ที่ได้รับการยอมรับ ฉันไม่เคยเห็นใครกำหนดFIFTEEN_DEGREES, ...
FrustratedWithFormsDesigner

5
@KChaloux ตัวอย่างจริงอาจกระจุยถ้ามีการเปลี่ยนจากองศาเป็นเรเดียน ภายใน 360 คุณจะแสดงการหมุนครบ 1 ครั้ง เนื่องจากมีการรับรองหลายรายการสำหรับค่าเดียวกันจึงควรดึงออกมา โดยเฉพาะอย่างยิ่งการพิจารณา 360PI อาจมีลักษณะเหมือน 2PI (180 การหมุน แต่ยังคงชี้ทิศทางเดียวกันในตอนท้าย) หรือ 360 การหมุนเหมือนการหมุน 1 ครั้ง แต่ผลข้างเคียงอาจแตกต่างกัน
Chris

14
บิตของชายฟางที่นั่นสองและ MINUS_ONE นั้นแย่มากเพราะการแทนที่ตัวเลขเวทย์มนตร์ด้วยการเรนเดอร์ในข้อความนั้นเป็นหลักสูตรที่งี่เง่า ชื่อของค่าคงที่จะต้องสื่อความหมายของมัน ยกเว้นตัวอย่างของคุณเกี่ยวกับข้อเท็จจริงพื้นฐานเกี่ยวกับตัวเลขผูกติดกับตัวเลขเฉพาะเหล่านั้นอย่างใกล้ชิดดังนั้นจึงไม่มีความหมายใด ๆ เลย
Michael Borgwardt

17
bool hasApples = apples > 0;

เห็นได้ชัดว่าไม่มีศูนย์หมายความว่าไม่มี ฉันพบว่าเข้าใจง่ายกว่า 0 ตัวแปรที่ชื่อ "ไม่มีค่า"


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

มันชัดเจน 0 คือตำแหน่งเริ่มต้น ฉันจะสับสนกับตัวแปรที่ชื่อว่า "firstPosition" ตัวแปรดังกล่าวทำให้ฉันสงสัยว่าตำแหน่งเริ่มต้นสามารถเปลี่ยนแปลงได้หรือไม่


14

ฉันขอแนะนำสามปัจจัยสำคัญในการตัดสินใจว่าสิ่งที่ควรจะมีการประกาศคงที่:

  1. เป็นจำนวนสิ่งที่เป็นตัวแทนได้อย่างแม่นยำและรัดกุม
  2. มีสถานการณ์ใดที่น่าเชื่อถือภายใต้ค่าที่จะต้องเปลี่ยน แต่ไม่จำเป็นต้องเขียนรหัสใหม่
  3. ใครบางคนที่เห็นตัวเลขนั้นสามารถเข้าใจได้เร็วกว่าหรือเร็วกว่าคนที่เห็นค่าคงที่ที่ตั้งชื่อไว้

บางสิ่งเช่นไพควรจะเขียนเป็นค่าคงที่ที่ตั้งชื่อแทนที่จะเป็นตัวอักษรตัวเลขเนื่องจากตัวอักษรที่เป็นตัวเลขมีแนวโน้มที่จะเป็น verbose โดยไม่จำเป็นไม่จำเป็นหรือทั้งสองอย่าง บางอย่างเช่นจำนวนของช่องในแคชน่าจะเป็นค่าคงที่ที่ตั้งชื่อไว้ (แม้ว่าจะดูหมายเหตุด้านล่าง) เพื่อให้สามารถขยายแคชได้โดยไม่ต้องแก้ไขโค้ดทั้งหมดที่ใช้ สิ่งที่ต้องการตัวเลข "4", "28" และ "29" ในงบอาจจะไม่ได้รับการตั้งชื่อค่าคงที่เนื่องจากการแสดงออกเป็นเกือบแน่นอนอ่านได้มากขึ้นกว่าif ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28; if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;โปรดทราบว่าผู้ดูแลมาตรฐานได้ระบุว่าความยาวของเดือนกุมภาพันธ์ 2100 ในปีนั้นจะไม่ตรงกับสูตรข้างต้น สิ่งกีดขวางในการจัดการวันที่อย่างถูกต้อง (เช่นรหัสจะไม่ได้รับการสะดุดด้วยจำนวนเต็มล้นหรือปัญหาอื่น ๆ )

ข้อแม้ที่สำคัญที่มีกฎข้อที่ 2 คือในบางกรณีรหัสอาจขึ้นอยู่กับตัวเลขที่มีการกำหนดรหัสตายตัวในลักษณะที่ไม่สามารถแสดงค่าคงที่ที่มีชื่อได้อย่างง่ายดาย ตัวอย่างเช่นวิธีที่คำนวณผลคูณของสองเวกเตอร์ที่ผ่านมาเป็นพารามิเตอร์ที่ไม่ต่อเนื่องจะมีความหมายเฉพาะเมื่อใช้กับเวกเตอร์สามมิติ จำนวนมิติที่ต้องการไม่ใช่ค่าที่สามารถเปลี่ยนแปลงได้อย่างมีความหมายโดยไม่ต้องเขียนรูทีนซ้ำทั้งหมด แม้ว่าหนึ่งคนเล็งเห็นถึงความต้องการที่เป็นไปได้สำหรับการคำนวณครอสโปรดัคต์ของเวกเตอร์ 4 มิติสามมิติการใช้ค่าคงที่ที่มีชื่อสำหรับค่า "3" จะทำเพียงเล็กน้อยเพื่อให้ง่ายต่อการตอบสนองความต้องการนั้น


4

ตามหลักการทั้งหมดนี้เป็นเรื่องของระดับ โดยทั่วไปแล้วตัวอักษรตัวเลขในซอร์สโค้ดจะยิ่งสงสัยว่ามันมีขนาดใหญ่กว่า ความยาวสูงสุดเช่น 10 หรือที่อยู่หน่วยความจำเช่น 0x587FB0 นั้นเป็นวิธีปฏิบัติที่ไม่ดี - เกือบจะแน่นอนว่าไม่ช้าก็เร็วคุณจะต้องทำซ้ำค่าเหล่านี้มากกว่าหนึ่งครั้งทำให้เกิดความเสี่ยงต่อความไม่ลงรอยกันและข้อผิดพลาดเล็กน้อยในสถานที่ การเปลี่ยนแปลง

0 อยู่ที่ปลายอีกด้านของเครื่องชั่ง มันยังสงสัยอยู่ แต่ก็ไม่มากนัก คุณใช้ 0 เป็นค่าเฝ้ายามหรือไม่? จากนั้นคุณควรใช้ค่าคงที่เชิงสัญลักษณ์แทนเนื่องจากค่าคงที่สามารถอธิบายความหมายได้ มันเป็นข้อตกลงทางวัฒนธรรมที่ยึดที่มั่นอย่างมากเช่น "0 หมายถึงการทำให้สำเร็จ" นั่นอาจจะตกลง มันหมายถึง "รายการแรกในคอลเล็กชัน" หรือไม่? นั่นอาจไม่เป็นอันตราย แต่ถ้ามีวิธีอื่นเช่นfirst()ฉันอาจจะชอบมัน


1
"คุณใช้ 0 เป็นค่าเฝ้ายามหรือไม่" <- คุณช่วยอธิบายความหมายของ "sentinel" ที่นี่ได้ไหม ฉันไม่พบคำจำกัดความที่ดูเหมือนจะตรงกัน
rory.ap

3

หมายเลขที่ไม่มีชื่อทุกตัวที่ไม่ปรากฏชัดในทันทีคือหมายเลขเวทย์มนตร์ มันโง่เล็กน้อยที่จะกำหนดตัวเลขที่มีความหมายที่ชัดเจนทันทีจากบริบท

ใน django (python web framework) ฉันอาจกำหนดฟิลด์ฐานข้อมูลบางอย่างด้วยหมายเลข raw เช่น:

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" ทุกครั้งต้องสงสัยและต้องได้รับการตรวจสอบอย่างรอบคอบ
Ross Patterson

1
@RossPatterson - นี่ไม่ใช่ C ที่เราเปรียบเทียบอย่างต่อเนื่องกับ var ทั่วโลก MAX_ARRAY_SIZE แต่เป็นเว็บเฟรมเวิร์กที่ดี สถานที่เดียวที่หมายเลขมายากลปรากฏขึ้นคือที่ที่คุณประกาศโมเดลฐานข้อมูล ทุกอย่างอื่นถูกเปรียบเทียบกับค่านี้ (เช่น 40 ปรากฏที่ใดในรหัส) นอกจากนี้โปรดทราบว่าคุณไม่สามารถเปลี่ยนตัวแปรนี้ได้อย่างง่ายดายโดยไม่ต้องทำการโยกย้ายสคีมาซึ่งเชื่อมโยงกับฐานข้อมูล ถ้าผมต้องการที่จะมีการเปลี่ยนแปลงที่จะบอกว่า 1 ตัวละครชื่อกลางที่เห็นได้ชัดทันทีหนึ่งในสถานที่ที่จะเปลี่ยนแปลงในรหัสการ40 1คุณต้องคิดถึงบริบท
dr jimbob

2
ขออภัยคุณทำผิดในสองประเด็น ก่อนอื่น OP ถามคำถาม "วิธีปฏิบัติในการเขียนโปรแกรม" ซึ่งไม่ได้ระบุภาษาใด ๆ พวกเขาพูดว่า "วิธีการ" ไม่ใช่ "ฟังก์ชั่น" ดังนั้นลองคิดอะไรเชิงวัตถุ แต่นั่นไม่ได้นำเราออกจากขอบเขตของข้อมูลที่มีหมายเลขเวทย์ ประการที่สองหากหมายเลขมายากลถูกอบเข้าสู่ฐานข้อมูล ( เช่นสคีมา) ก็จะยิ่งแย่ลงหากมีรหัส สิ่งที่ถูกต้องที่จะทำคือการได้รับเวทมนตร์เกือบ - ค่าคงที่จากแหล่งที่มา - ทั้งฐานข้อมูลตัวเองหรือโมดูลสคีมาที่รวมศูนย์ค่าคงที่ทั้งหมดเหล่านั้น
Ross Patterson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.