Saya baru-baru ini menjalani tes di kelas saya. Salah satu masalah adalah sebagai berikut:
Diberi nomor n , tulis fungsi dalam C / C ++ yang mengembalikan jumlah angka dari kuadrat . (Berikut ini penting). The berbagai dari n adalah [- (10 ^ 7), 10 ^ 7]. Contoh: Jika n = 123, fungsi Anda harus mengembalikan 14 (1 ^ 2 + 2 ^ 2 + 3 ^ 2 = 14).
Ini adalah fungsi yang saya tulis:
int sum_of_digits_squared(int n)
{
int s = 0, c;
while (n) {
c = n % 10;
s += (c * c);
n /= 10;
}
return s;
}
Tampak benar bagiku. Jadi sekarang tes kembali dan saya menemukan bahwa guru tidak memberi saya semua poin karena alasan yang saya tidak mengerti. Menurutnya, agar fungsi saya menjadi lengkap, saya seharusnya menambahkan detail berikut:
int sum_of_digits_squared(int n)
{
int s = 0, c;
if (n == 0) { //
return 0; //
} //
// THIS APPARENTLY SHOULD'VE
if (n < 0) { // BEEN IN THE FUNCTION FOR IT
n = n * (-1); // TO BE CORRECT
} //
while (n) {
c = n % 10;
s += (c * c);
n /= 10;
}
return s;
}
Argumen untuk ini adalah bahwa angka n berada dalam kisaran [- (10 ^ 7), 10 ^ 7], sehingga dapat menjadi angka negatif. Tapi saya tidak melihat di mana versi saya sendiri dari fungsi gagal. Jika saya mengerti benar, makna while(n)
adalah while(n != 0)
, tidak while (n > 0)
, sehingga dalam versi saya fungsi jumlah n tidak akan gagal untuk masuk ke loop. Itu akan bekerja sama saja.
Kemudian, saya mencoba kedua versi fungsi di komputer saya di rumah dan saya mendapatkan jawaban yang persis sama untuk semua contoh yang saya coba. Jadi, sum_of_digits_squared(-123)
sama dengan sum_of_digits_squared(123)
(yang lagi-lagi sama dengan 14
) (bahkan tanpa detail yang sepertinya harus saya tambahkan). Memang, jika saya mencoba untuk mencetak pada layar angka-angka dari (dari yang paling penting sampai yang paling penting), dalam 123
kasus yang saya dapatkan 3 2 1
dan dalam -123
kasus yang saya dapatkan -3 -2 -1
(yang sebenarnya agak menarik). Tetapi dalam masalah ini tidak masalah karena kita mengkuadratkan angka.
Jadi siapa yang salah?
EDIT : Buruk saya, saya lupa menentukan dan tidak tahu itu penting. Versi C yang digunakan di kelas kami dan tes harus C99 atau lebih baru . Jadi saya kira (dengan membaca komentar) bahwa versi saya akan mendapatkan jawaban yang benar dengan cara apa pun.
n = n * (-1)
adalah cara konyol untuk menulisn = -n
; Hanya seorang akademisi yang akan memikirkannya. Apalagi menambahkan tanda kurung yang berlebihan.n = n * (-1)
? Wut ??? Apa yang dicari profesor Anda adalah ini: `n = -n '. Bahasa C memiliki operator minus unary.Jawaban:
Merangkum diskusi yang telah meresap dalam komentar:
n == 0
. Thewhile(n)
tes akan menangani kasus itu dengan sempurna.%
dengan operan negatif didefinisikan secara berbeda. Pada beberapa sistem lama (termasuk, terutama, awal Unix pada PDP-11, di mana Dennis Ritchie awalnya dikembangkan C), hasila % b
itu selalu dalam kisaran[0 .. b-1]
, yang berarti bahwa -123% 10 adalah 7. Pada sistem seperti itu, tes di mukan < 0
akan diperlukan.Tapi peluru kedua hanya berlaku untuk masa-masa sebelumnya. Dalam versi terkini dari standar C dan C ++, pembagian integer didefinisikan untuk memotong ke 0, sehingga ternyata
n % 10
dijamin memberi Anda digit terakhir (mungkin negatif)n
bahkan ketikan
negatif.Jadi jawaban untuk pertanyaan "Apa artinya
while(n)
?" adalah "Persis sama denganwhile(n != 0)
" , dan jawaban untuk "Apakah kode ini berfungsi dengan baik untuk negatif maupun positifn
?" adalah "Ya, di bawah kompiler modern yang memenuhi standar apa pun." Jawaban untuk pertanyaan "Lalu mengapa instruktur menandainya?" mungkin mereka tidak menyadari adanya definisi ulang bahasa yang signifikan yang terjadi pada C pada tahun 1999 dan C ++ pada tahun 2010 atau lebih.sumber
n == 0
setidaknya membuatnya segera dan jelas bagi pembaca apa pun yang terjadi dalam kasus itu. Tanpa itu, pembaca harus meyakinkan diri sendiri bahwa loop memang dilewati, dan nilai default yangs
dikembalikan adalah yang benar.n=0
. Memperkenalkan cabang yang tidak perlu dan komplikasi tidak membuat kode lebih mudah, itu membuatnya lebih sulit karena sekarang Anda tidak hanya harus menunjukkan bahwa algoritma umum sudah benar, Anda juga harus memikirkan semua kasus khusus secara terpisah.Kode Anda baik-baik saja
Anda benar sekali dan guru Anda salah. Sama sekali tidak ada alasan sama sekali untuk menambahkan kompleksitas ekstra, karena itu tidak mempengaruhi hasilnya sama sekali. Bahkan memperkenalkan bug. (Lihat di bawah)
Pertama, pemeriksaan terpisah apakah
n
nol jelas sama sekali tidak perlu dan ini sangat mudah untuk diwujudkan. Sejujurnya, saya benar-benar mempertanyakan kompetensi guru Anda jika dia keberatan dengan hal ini. Tetapi saya kira setiap orang dapat memiliki otak yang kentut dari waktu ke waktu. Namun, saya TIDAK berpikir ituwhile(n)
harus diubahwhile(n != 0)
karena menambah sedikit kejelasan ekstra bahkan tanpa biaya garis tambahan. Ini hal kecil.Yang kedua sedikit lebih bisa dimengerti, tetapi dia masih salah.
Inilah yang dikatakan standar C11 6.5.5.p6 :
Catatan kaki mengatakan ini:
Pemotongan ke nol berarti bahwa nilai absolut untuk
a/b
sama dengan nilai absolut(-a)/b
untuk semuaa
danb
, yang pada gilirannya berarti bahwa kode Anda baik-baik saja.Modulo adalah matematika yang mudah, tetapi mungkin berlawanan dengan intuisi
Namun, guru Anda memang memiliki poin bahwa Anda harus berhati-hati, karena fakta bahwa Anda mengkuadratkan hasilnya sebenarnya sangat penting di sini. Menghitung
a%b
menurut definisi di atas adalah matematika mudah, tetapi itu mungkin bertentangan dengan intuisi Anda. Untuk perkalian dan pembagian, hasilnya positif jika operan memiliki tanda yang sama. Tetapi ketika datang ke modulo, hasilnya memiliki tanda yang sama dengan operan pertama . Operan kedua tidak mempengaruhi tanda sama sekali. Misalnya,7%3==1
tapi(-7)%(-3)==(-1)
.Ini cuplikan yang menunjukkan:
Jadi, ironisnya, gurumu membuktikan pendapatnya dengan salah.
Kode guru Anda cacat
Ya, sebenarnya. Jika inputnya
INT_MIN
DAN arsitekturnya adalah pelengkap dua DAN pola bit di mana bit tanda adalah 1 dan semua nilai bit adalah 0 BUKAN nilai jebakan (menggunakan pelengkap dua tanpa nilai jebakan sangat umum) maka kode guru Anda akan menghasilkan perilaku yang tidak terdefinisi di teleponn = n * (-1)
. Kode Anda - jika sedikit - lebih baik dari miliknya. Dan mempertimbangkan memperkenalkan bug kecil dengan membuat kode tidak perlu rumit dan mendapatkan nilai nol, saya akan mengatakan bahwa kode Anda JAUH lebih baik.Dengan kata lain, dalam kompilasi di mana INT_MIN = -32768 (meskipun fungsi yang dihasilkan tidak dapat menerima input yang <-32768 atau> 32767), input yang valid dari -32768 menyebabkan perilaku tidak terdefinisi, karena hasil dari - (- 32768i16) tidak dapat dinyatakan sebagai bilangan bulat 16-bit. (Sebenarnya, -32768 mungkin tidak akan menyebabkan hasil yang salah, karena - (- 32768i16) biasanya mengevaluasi ke -32768i16, dan program Anda menangani angka negatif dengan benar.) (SHRT_MIN bisa -32768 atau -32767, tergantung pada kompilernya)
Tetapi guru Anda secara eksplisit menyatakan bahwa
n
bisa berada dalam kisaran [-10 ^ 7; 10 ^ 7]. Bilangan bulat 16-bit terlalu kecil; Anda harus menggunakan [setidaknya] integer 32-bit. Penggunaannyaint
mungkin membuat kode-nya aman, kecuali ituint
belum tentu integer 32-bit. Jika Anda mengkompilasi untuk arsitektur 16-bit, kedua cuplikan kode Anda cacat. Tetapi kode Anda masih jauh lebih baik karena skenario ini memperkenalkan kembali bug dengan yangINT_MIN
disebutkan di atas dengan versinya. Untuk menghindari ini, Anda bisa menulislong
alih-alihint
, yang merupakan bilangan bulat 32-bit pada kedua arsitektur. Along
dijamin dapat memiliki nilai apa pun dalam kisaran [-2147483647; 2147483647]. C11 Standar 5.2.4.2.1LONG_MIN
sering-2147483648
tetapi nilai maksimum yang diizinkan (ya, maksimum, ini adalah angka negatif)LONG_MIN
adalah2147483647
.Perubahan apa yang akan saya lakukan pada kode Anda?
Kode Anda baik-baik saja, jadi ini bukan keluhan. Lebih seperti itu jika saya benar-benar perlu mengatakan apa pun tentang kode Anda, ada beberapa hal kecil yang dapat membuatnya sedikit lebih jelas.
n
menjadin!=0
. Secara semantik, ini setara 100%, tetapi membuatnya sedikit lebih jelas.c
(yang saya ganti namanyadigit
) ke dalam loop sementara karena itu hanya digunakan di sana.long
untuk memastikannya dapat menangani seluruh rangkaian input.Sebenarnya, ini bisa sedikit menyesatkan karena - seperti yang disebutkan di atas - variabel
digit
bisa mendapatkan nilai negatif, tetapi digit itu sendiri tidak pernah positif atau negatif. Ada beberapa cara untuk mengatasi hal ini, tetapi ini BENAR-BENAR membingungkan, dan saya tidak akan peduli dengan detail sekecil itu. Terutama fungsi terpisah untuk digit terakhir terlalu jauh. Ironisnya, ini adalah salah satu hal yang dipecahkan oleh kode guru Anda.sum += (digit * digit)
kesum += ((n%10)*(n%10))
dan lewati variabeldigit
sepenuhnya.digit
jika negatif. Tapi saya akan sangat menyarankan agar kode tidak lebih kompleks hanya untuk membuat nama variabel masuk akal. Itu bau kode yang SANGAT kuat.int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }
Ini berguna jika Anda ingin menggunakan fungsi itu di tempat lain.c
seperti yang Anda lakukan semula. Nama variabel itu tidak memberikan informasi yang berguna, tetapi di sisi lain, itu juga tidak menyesatkan.Tetapi jujur saja, pada titik ini Anda harus beralih ke pekerjaan yang lebih penting. :)
sumber
INT_MIN
dan arsitekturnya menggunakan komplemen dua (yang sangat umum) maka kode guru Anda akan menghasilkan perilaku yang tidak terdefinisi. Aduh. Itu akan meninggalkan bekas. ;-)(a/b)*b + a%b ≡ a
, kode OP juga bergantung pada fakta bahwa/
putaran menuju nol, dan itu(-c)*(-c) ≡ c*c
. Ini bisa dikatakan bahwa pemeriksaan ekstra dibenarkan meskipun standar menjamin semua itu, karena itu cukup non-jelas. (Tentu saja dapat juga diperdebatkan bahwa seharusnya ada komentar yang menghubungkan bagian standar yang relevan, tetapi pedoman gaya bervariasi.)%
dan/
operator dapat dikompilasi menjadi hanyaidiv
pada x86, atausdiv
pada ARM atau apa pun. Namun, itu tidak terkait dengan banyak kode-gen yang lebih cepat untuk pembagi waktu kompilasi-konstan)Saya tidak sepenuhnya menyukai versi Anda atau versi guru Anda. Versi guru Anda tidak perlu melakukan tes tambahan yang Anda tunjukkan dengan benar. Operator mod C bukan mod matematika yang tepat: angka negatif mod 10 akan menghasilkan hasil negatif (modulus matematika yang tepat selalu non-negatif). Tetapi karena Anda tetap mengkuadratkannya, tidak ada perbedaan.
Tapi ini jauh dari jelas, jadi saya akan menambahkan kode Anda bukan cek guru Anda, tetapi komentar besar yang menjelaskan mengapa itu berhasil. Misalnya:
/ * CATATAN: Ini berfungsi untuk nilai negatif, karena modulus menjadi kuadrat * /
sumber
%
paling baik disebut sebagai sisa , karena memang itu, bahkan untuk tipe yang ditandatangani.-7 % 10
sebenarnya akan-7
lebih daripada 3.%
bukan operator C.CATATAN: SEBAGAI saya menulis jawaban ini, Anda mengklarifikasi bahwa Anda menggunakan C. Mayoritas jawaban saya adalah tentang C ++. Namun, karena judul Anda masih memiliki C ++ dan pertanyaannya masih ditandai C ++, saya tetap memilih untuk menjawab kalau-kalau ini masih berguna bagi orang lain, terutama karena sebagian besar jawaban yang saya lihat sampai sekarang sebagian besar tidak memuaskan.
Di zaman modern C + + (Catatan: Saya tidak benar-benar tahu di mana C berdiri pada ini), profesor Anda tampaknya salah dalam kedua hal.
Pertama adalah bagian ini di sini:
Dalam C ++, ini pada dasarnya sama dengan :
Itu berarti saat Anda setara dengan sesuatu seperti ini:
Itu berarti karena Anda hanya keluar di if Anda ketika sementara tidak akan mengeksekusi pula, sebenarnya tidak ada alasan untuk menempatkan ini jika di sini, karena apa yang Anda lakukan setelah loop dan di jika tetap sama. Meskipun saya harus mengatakan bahwa karena alasan tertentu ini berbeda, Anda harus memilikinya jika.
Jadi sungguh, pernyataan if ini tidak terlalu berguna kecuali saya salah.
Bagian kedua adalah di mana semuanya menjadi berbulu:
Inti dari masalah ini adalah apa output dari modulus dari output angka negatif.
Dalam C ++ modern, ini tampaknya sebagian besar didefinisikan dengan baik :
Dan kemudian:
Seperti yang ditunjukkan oleh poster jawaban yang dikutip dengan benar, bagian penting dari persamaan ini di sini:
Mengambil contoh dari kasus Anda, Anda akan mendapatkan sesuatu seperti ini:
Satu-satunya tangkapan adalah baris terakhir:
Itu artinya dalam kasus seperti ini, hanya pertanda tampaknya sudah ditentukan implementasi. Itu seharusnya tidak menjadi masalah dalam kasus Anda karena, karena Anda tetap mengkuadratkan nilai ini.
Karena itu, perlu diingat bahwa ini tidak selalu berlaku untuk versi C ++ yang lebih lama, atau C99. Jika itu yang digunakan profesor Anda, itu bisa menjadi alasannya.
EDIT: Tidak, saya salah. Ini tampaknya juga berlaku untuk C99 atau lebih baru :
Dan tempat lain :
Jadi ya. Bahkan di C99, ini sepertinya tidak mempengaruhi Anda. Persamaannya sama.
sumber
(-1)%10
dapat menghasilkan-1
atau1
; itu berarti dapat menghasilkan-1
atau9
, dan dalam kasus terakhir(-1)/10
akan menghasilkan-1
dan kode OP tidak akan pernah berakhir.(a/b)*b + a%b == a
, lalu biarkana=-1; b=10
, memberi(-1/10)*10 + (-1)%10 == -1
. Sekarang, jika-1/10
memang dibulatkan (ke -inf), maka kita miliki(-1/10)*10 == -10
, dan Anda harus memiliki(-1)%10 == 9
persamaan pertama yang cocok. Seperti negara jawaban lainnya , ini bukan cara kerjanya dalam standar saat ini, tetapi ini adalah cara kerjanya. Ini tidak benar-benar tentang tanda sisa seperti itu, tapi bagaimana putaran divisi dan apa sisanya kemudian memiliki untuk menjadi untuk memenuhi persamaan.(-1)*10+9=-1
, jadi pilihan(-1)/10=-1
dan(-1)%10=9
tidak melanggar persamaan yang mengatur. Di sisi lain pilihan itu(-1)%10=1
tidak dapat memenuhi persamaan yang mengatur tidak peduli bagaimana(-1)/10
dipilih; tidak ada bilangan bulatq
seperti ituq*10+1=-1
.Seperti yang telah ditunjukkan orang lain, perlakuan khusus untuk n == 0 adalah omong kosong, karena untuk setiap programmer C yang serius jelas bahwa "while (n)" melakukan pekerjaan.
Perilaku untuk n <0 tidak begitu jelas, itu sebabnya saya lebih suka melihat 2 baris kode:
atau setidaknya komentar:
Jujur, pada jam berapa Anda mulai mempertimbangkan bahwa n mungkin negatif? Saat menulis kode atau ketika membaca komentar guru Anda?
sumber
Ini mengingatkan saya pada tugas yang gagal
Kembali di tahun 90-an. Dosen telah bertunas tentang loop dan, singkatnya, tugas kami adalah untuk menulis fungsi yang akan mengembalikan jumlah digit untuk setiap bilangan bulat yang diberikan> 0.
Jadi, misalnya, jumlah digit
321
akan menjadi3
.Meskipun tugas hanya mengatakan untuk menulis fungsi yang mengembalikan jumlah digit, harapannya adalah bahwa kita akan menggunakan loop yang membaginya dengan 10 sampai ... Anda mendapatkannya, seperti yang dicakup oleh kuliah .
Tetapi menggunakan loop tidak secara eksplisit dinyatakan jadi saya:
took the log, stripped away the decimals, added 1
dan kemudian dicerca di depan seluruh kelas.Intinya, tujuan dari tugas ini adalah untuk menguji pemahaman kita tentang apa yang telah kita pelajari selama kuliah . Dari kuliah yang saya terima, saya tahu bahwa guru komputer itu sedikit brengsek (tapi mungkin brengsek dengan rencana?)
Dalam situasi Anda:
Saya pasti akan memberikan dua jawaban:
sumber
Umumnya dalam penugasan tidak semua tanda diberikan hanya karena kode berfungsi. Anda juga mendapatkan nilai untuk membuat solusi mudah dibaca, efisien dan elegan. Hal-hal ini tidak selalu saling eksklusif.
Satu saya tidak bisa strees cukup adalah "menggunakan nama variabel yang bermakna" .
Dalam contoh Anda itu tidak membuat banyak perbedaan, tetapi jika Anda mengerjakan proyek dengan jutaan baris pembacaan kode menjadi sangat penting.
Hal lain yang cenderung saya lihat dengan kode C adalah orang yang mencoba terlihat pintar. Daripada menggunakan while (n! = 0) saya akan menunjukkan kepada semua orang betapa pandainya saya dengan menulis while (n) karena itu artinya hal yang sama. Baik itu dalam kompiler yang Anda miliki tetapi seperti yang Anda sarankan versi lama guru Anda belum mengimplementasikannya dengan cara yang sama.
Contoh umum adalah mereferensikan indeks dalam array sambil menambahkannya pada saat yang bersamaan; Angka [i ++] = iPrime;
Sekarang, programmer berikutnya yang bekerja pada kode harus tahu apakah saya bertambah sebelum atau setelah penugasan, supaya seseorang bisa pamer.
Satu megabyte ruang disk lebih murah dari pada gulungan kertas toilet, lebih jelas daripada mencoba menghemat ruang, sesama programmer Anda akan lebih bahagia.
sumber
++i
kenaikan sebelum evaluasi dani++
kenaikan sesudahnya.while(n)
juga merupakan fitur bahasa yang umum. Berdasarkan logika seperti ini, saya telah melihat banyak kode sepertiif (foo == TRUE)
. Saya setuju kembali: nama variabel.while(n)
bukan contoh terburuk untuk itu (saya "lebih suka"if(strcmp(one, two))
lebih)i++
dan++i
memodifikasi kode C yang harus digunakan dalam produksi.Saya tidak akan berdebat tentang apakah definisi asli atau modern tentang '%' lebih baik tetapi siapa pun yang menulis dua pernyataan kembali ke dalam fungsi singkat seperti itu seharusnya tidak mengajarkan pemrograman C sama sekali. Pengembalian ekstra adalah pernyataan goto dan kami tidak menggunakan goto dalam C. Selanjutnya kode tanpa cek nol akan memiliki hasil yang sama, pengembalian ekstra membuatnya lebih sulit untuk dibaca.
sumber
int findChar(char *str, char c) { if(!str) return -1; int i=0; while(str[i]) { if(str[i] == c) return i; i++; } return -1; }
Pernyataan masalah membingungkan, tetapi contoh numerik mengklarifikasi arti dari jumlah digit dari angka kuadrat . Ini versi yang ditingkatkan:
Fungsi yang Anda tulis baik-baik saja kecuali untuk 2 detail:
long
untuk mengakomodasi semua nilai dalam rentang yang ditentukan karena tipelong
dijamin oleh Standar C untuk memiliki setidaknya 31 bit nilai, karenanya rentang yang cukup untuk mewakili semua nilai dalam [-10 7 , 10 7 ] . (Perhatikan bahwa jenisint
tersebut cukup untuk jenis pengembalian, yang nilainya maksimum568
.)%
operan negatif adalah non-intuitif dan spesifikasinya bervariasi antara Standar C99 dan edisi sebelumnya. Anda harus mendokumentasikan mengapa pendekatan Anda valid bahkan untuk input negatif.Ini adalah versi yang dimodifikasi:
Jawaban guru memiliki banyak kelemahan:
int
mungkin memiliki kisaran nilai yang tidak memadai.0
.n = INT_MIN
.Mengingat kendala tambahan dalam pernyataan masalah (C99 dan rentang nilai untuk
n
), hanya kelemahan pertama yang menjadi masalah. Kode tambahan masih menghasilkan jawaban yang benar.Anda harus mendapatkan nilai bagus dalam tes ini, tetapi penjelasan diperlukan dalam tes tertulis untuk menunjukkan pemahaman Anda tentang masalah negatif
n
, jika tidak guru mungkin berasumsi bahwa Anda tidak sadar dan hanya beruntung. Dalam ujian lisan, Anda akan mendapat pertanyaan dan jawaban Anda akan berhasil.sumber