Apakah setiap angka dalam kode dianggap sebagai "angka ajaib"?

21

Jadi setiap angka dalam kode yang kita kirim ke suatu metode sebagai argumen dianggap sebagai Angka Ajaib? Bagi saya, seharusnya tidak. Saya pikir jika beberapa angka katakanlah itu untuk panjang minimum nama pengguna dan kami mulai menggunakan "6" dalam kode ... maka ya kami memiliki masalah pemeliharaan dan di sini "6" adalah angka ajaib .... tetapi jika kita memanggil metode yang salah satu argumennya menerima bilangan bulat misalnya sebagai anggota ke-i koleksi dan kemudian kita meneruskan "0" ke pemanggilan metode itu, dalam hal ini saya tidak melihat "0" sebagai sihir. jumlah. Apa yang kamu pikirkan?

Blake
sumber
4
Dalam contoh Anda, apa yang diwakili oleh 0?
Aaron Kurtzhals
2
Dalam kasus yang Anda ilustrasikan, "0" tidak memiliki sifat magis sama sekali.
Tulains Córdova
4
Semuanya kecuali 0,1 dan 42 adalah sihir
Mawg

Jawaban:

43

Jika arti angka itu sangat jelas dalam konteksnya, saya tidak berpikir itu masalah "angka ajaib".

Contoh: Misalkan Anda mencoba untuk mendapatkan substring dari string, dari awal hingga beberapa token dan kode terlihat seperti ini (bahasa imajiner dan perpustakaan):

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

Dalam konteks ini, arti angka 0 cukup jelas. Saya kira Anda dapat mendefinisikan START_OF_SUBSTRINGdan menetapkannya ke 0, tetapi dalam kasus ini saya pikir itu akan berlebihan (meskipun itu akan menjadi pendekatan yang benar jika Anda tahu bahwa awal substring Anda mungkin tidak 0, tetapi itu tergantung pada spesifikasi dari situasi Anda).

Contoh lain mungkin jika Anda mencoba menentukan apakah suatu bilangan genap atau ganjil. Penulisan:

isEven := x % 2;

tidak seaneh:

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

Menguji angka negatif sebagai

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

juga terasa aneh bagi saya, saya lebih suka melihat

isNegativeInt := i <= -1;
FrustratedWithFormsDesigner
sumber
6
Untuk melempar contoh lain di sana, dalam kode di mana Anda secara eksplisit bekerja dengan derajat pada lingkaran, akan adil untuk menggunakan angka seperti 360untuk menandai rotasi penuh dengan pemahaman bahwa kebanyakan orang akan tahu apa artinya (meskipun ini adalah kasus di mana tidak ada ruginya memberikan konstanta)
KChaloux
11
KChaloux: Jika saya bisa, saya akan -1 komentar Anda. 360 adalah angka ajaib. Jika 360 menjadi nilai untuk konstanta lain, maka Anda memiliki 2 set untuk 360 yang tidak terkait dan tidak dapat dibedakan. Junior datang, pergi "Itu adalah angka ajaib", pencarian global dan ganti 360 dengan "Degrees_in_Circle", jalankan semua unit dan uji regresi, semua pass - memberikan perbaikan kode. Kode sekarang sarapan anjing, dan kita semua tahu apa yang terjadi setelah waktu yang singkat .......
mattnz
4
@ mattnz: Mudah-mudahan perubahan kode skala besar semacam itu akan cepat ditangkap (mudah-mudahan selama review kode, jika mereka junior itu) jauh sebelum itu mulai diproduksi. Saya pikir seseorang yang akan melakukan itu dalam konteks itu mungkin juga akan menggantikan 0dalam konteks contoh substring saya. Dalam hal ini, mungkin ini setidaknya jumlah kerusakan yang mereka dapat menyebabkan. Sudah lama sejak saya melakukan pengkodean yang melakukan perhitungan geometris, tetapi umumnya, nilai 15, 30, 45, 60, 90, 180, 360 adalah konstanta yang diterima. Saya belum pernah melihat orang yang mendefinisikan FIFTEEN_DEGREES, ...
FrustratedWithFormsDesigner
5
@KChaloux Contohnya mungkin benar-benar berantakan jika ada perubahan dari Derajat ke Radian. Pada 360, Anda menyatakan 1 rotasi lengkap. Karena ada beberapa representasi untuk nilai yang sama, itu harus ditarik. Terutama mengingat 360PI bisa terlihat sama dengan 2PI (180 rotasi tetapi tetap menunjuk ke arah yang sama pada akhirnya), atau 360 rotasi sama dengan 1 rotasi, tetapi efek samping mungkin berbeda.
Chris
14
Sedikit penipu di sana, DUA dan MINUS_ONE benar-benar buruk karena mengganti angka ajaib dengan rendering dalam teks adalah OF COURSE idiot. Nama konstanta harus menyampaikan maknanya. Kecuali contoh Anda adalah tentang fakta mendasar tentang angka, terkait erat dengan angka-angka tertentu saja, jadi tidak ada artinya selain itu.
Michael Borgwardt
17
bool hasApples = apples > 0;

Jelas nol berarti tidak ada. Saya menemukan 0 lebih mudah dipahami daripada variabel bernama "absen".


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

Jelas 0 adalah posisi awal. Saya akan bingung dengan variabel bernama "firstPosition". Variabel seperti itu akan membuat saya bertanya-tanya apakah posisi awal bisa berubah.

mike30
sumber
14

Saya akan menyarankan tiga faktor utama dalam memutuskan apakah sesuatu harus merupakan deklarasi konstan:

  1. Apakah angka itu sesuatu yang tepat dan ringkas diwakili
  2. Apakah ada skenario yang masuk akal di mana nilainya harus berubah, tetapi kode tidak harus ditulis ulang
  3. Apakah seseorang yang melihat nomor tersebut cenderung mengenalinya lebih cepat atau kurang cepat daripada seseorang yang melihat konstanta bernama

Sesuatu seperti pi mungkin harus ditulis sebagai konstanta bernama, bukan sebagai literal numerik, karena literal numerik cenderung tidak perlu bertele-tele, tidak perlu tidak tepat, atau keduanya. Sesuatu seperti jumlah slot dalam cache kemungkinan harus berupa konstanta bernama (meskipun lihat catatan di bawah) untuk memungkinkan kemungkinan memperluas cache tanpa harus memodifikasi semua kode yang menggunakannya. Hal-hal seperti angka "4", "28", dan "29" dalam pernyataan if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28;itu mungkin tidak boleh disebut konstanta, karena ungkapan itu hampir pasti lebih mudah dibaca daripada if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;. Perhatikan bahwa pengelola standar telah mengindikasikan bahwa panjang Februari 2100 di tahun itu tidak akan cocok dengan rumus di atas, halangan untuk menangani tanggal dengan benar (mis. kode tidak akan tersandung oleh integer overflow atau masalah lainnya)

Peringatan penting dengan aturan # 2 adalah bahwa dalam beberapa kasus kode dapat mengandalkan angka-angka hard-code dengan cara yang tidak dapat dengan mudah diwakili oleh konstanta bernama. Sebagai contoh, suatu metode yang menghitung produk silang dari dua vektor yang dilewatkan sebagai parameter diskrit hanya akan bermakna ketika digunakan pada vektor tiga dimensi. Jumlah dimensi yang diperlukan bukanlah nilai yang dapat diubah secara bermakna tanpa sepenuhnya menulis ulang rutinitas. Bahkan jika seseorang meramalkan kemungkinan kebutuhan untuk menghitung produk silang dari tiga vektor 4-dimensi, menggunakan konstanta bernama untuk nilai "3" tidak akan banyak membantu membuatnya lebih mudah untuk memenuhi kebutuhan itu.

supercat
sumber
4

Ini, seperti semua prinsip, adalah masalah derajat. Secara umum, jumlah literal dalam kode sumber lebih dicurigai semakin besar. Panjang maksimum seperti 10 atau alamat memori seperti 0x587FB0 jelas merupakan praktik yang buruk - hampir pasti bahwa cepat atau lambat Anda harus mengulangi nilai-nilai ini lebih dari sekali, menciptakan risiko ketidakcocokan dan kesalahan halus yang diperkenalkan di tempat-tempat yang tidak berubah.

0 berada di ujung skala; itu masih mencurigakan tetapi tidak sebanyak. Apakah Anda menggunakan 0 sebagai nilai sentinel? Maka Anda mungkin harus menggunakan konstanta simbolis sebagai gantinya, hanya karena konstanta dapat menjelaskan apa artinya. Apakah ini perjanjian budaya yang sangat mengakar seperti "0 berarti penyelesaian yang sukses"? Itu mungkin baik. Apakah ini berarti "item pertama dalam koleksi"? Itu mungkin tidak berbahaya, tetapi jika ada metode alternatif seperti first()saya mungkin lebih suka itu.

Kilian Foth
sumber
1
"Apakah Anda menggunakan 0 sebagai nilai sentinel?" <- Bisakah kamu menjelaskan apa yang kamu maksud dengan "sentinel" di sini? Saya tidak dapat menemukan definisi yang cocok.
rory.ap
3

Setiap angka yang tidak disebutkan namanya yang tidak segera terlihat dari konteks adalah angka ajaib. Agak konyol untuk mendefinisikan angka-angka yang memiliki makna yang segera jelas dari konteks.

Dalam Django (kerangka web python), saya dapat mendefinisikan beberapa bidang basis data dengan angka mentah seperti:

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

yang lebih jelas (dan praktik yang disarankan ) daripada mengatakan

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) 

karena saya tidak mungkin perlu mengubah panjang (dan selalu dapat dibandingkan dengan max_lengthbidang). Jika saya perlu mengubah panjang bidang setelah awalnya menyebarkan aplikasi, saya harus mengubahnya tepat satu lokasi per bidang dalam kode Django saya, dan kemudian menulis migrasi untuk mengubah skema DB. Jika saya perlu referensi max_lengthbidang yang ditentukan dari jenis objek, saya bisa melakukannya secara langsung - jika bidang-bidang itu mendefinisikan Personkelas, saya bisa menggunakan Person._meta.get_field('firstname').max_lengthuntuk mendapatkanmax_lengthsedang digunakan (yang didefinisikan di satu tempat). Fakta bahwa 40 yang sama digunakan untuk banyak bidang tidak relevan karena saya mungkin ingin mengubahnya secara mandiri. Panjang nama depan tidak boleh bergantung pada panjang middlename atau nama belakang; mereka adalah nilai yang terpisah dan dapat berubah secara independen.

Seringkali indeks array dapat menggunakan angka yang tidak disebutkan namanya; seperti jika saya memiliki file data CSV yang ingin saya masukkan ke dalam kamus python, dengan elemen pertama di baris sebagai kamus yang keyakan saya tulis:

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

Tentu saya bisa memberi nama index_column = 0dan melakukan sesuatu seperti:

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

atau lebih buruk menentukan after_index_col = index_col + 1untuk menyingkirkan index_col+1, tetapi itu tidak membuat kode lebih jelas dalam pandangan saya. Juga, jika saya memberi index_colnama, saya lebih baik membuat kode bekerja bahkan jika kolomnya tidak 0 (maka row[:index_col] +bagian itu).

dr jimbob
sumber
7
Sebenarnya, max_lngth=40vs. max_length=MAX_LENGTH_NAMEadalah contoh klasik dari angka ajaib yang berteriak menjadi simbol. Hari itu akan tiba ketika Anda ingin mendukung 45 nama karakter, dan sekarang setiap penggunaan "40" dicurigai dan harus diperiksa dengan cermat.
Ross Patterson
1
@RossPatterson - Ini bukan C di mana kami terus-menerus membandingkannya dengan var global MAX_ARRAY_SIZE, tetapi kerangka kerja web yang layak. Satu-satunya tempat nomor ajaib muncul adalah tempat Anda mendeklarasikan model database; segala sesuatu yang dibandingkan dengan nilai ini (misalnya, 40 muncul di tempat lain dalam kode). Perhatikan juga, Anda tidak dapat mengubah variabel ini dengan mudah tanpa melakukan migrasi skema karena terkait dengan DB. Jika saya ingin mengubah mengatakan nama tengah 1 karakter segera jelas satu tempat untuk perubahan kode 40untuk 1. Anda harus memikirkan konteks.
dr jimbob
2
Maaf, Anda salah dalam dua poin. Pertama, OP mengajukan pertanyaan "praktik pemrograman" yang tidak menentukan bahasa apa pun. Mereka mengatakan "metode", bukan "fungsi", jadi mari kita anggap sesuatu berorientasi objek, tapi itu tidak membawa kita keluar dari dunia data bernomor sihir. Kedua, jika angka ajaib dimasukkan ke dalam basis data ( misalnya , skema), maka lebih buruk untuk memilikinya dalam kode. Hal yang benar untuk dilakukan adalah mendapatkan keajaiban hampir-konstan dari sumbernya - baik database itu sendiri atau modul skema yang memusatkan semua konstanta-bahwa-akan-bervariasi-selama-kode-seumur hidup.
Ross Patterson