รหัสของคุณใช้ได้อย่างสมบูรณ์แบบ
คุณถูกต้องอย่างแน่นอนและครูของคุณผิด ไม่มีเหตุผลใด ๆ เลยที่จะเพิ่มความซับซ้อนเป็นพิเศษเนื่องจากไม่มีผลต่อผลลัพธ์เลย มันยังแนะนำข้อผิดพลาด (ดูด้านล่าง)
ก่อนอื่นการตรวจสอบแยกn
เป็นศูนย์เห็นได้ชัดว่าไม่จำเป็นอย่างสมบูรณ์และนี่เป็นเรื่องง่ายที่จะตระหนัก ความจริงแล้วฉันถามความสามารถของครูถ้าเขามีข้อคัดค้านเกี่ยวกับเรื่องนี้ แต่ฉันคิดว่าทุกคนสามารถมีผายลมในสมองเป็นครั้งคราว อย่างไรก็ตามฉันคิดว่าwhile(n)
ควรเปลี่ยนเป็นwhile(n != 0)
เพราะมันเพิ่มความชัดเจนพิเศษเล็กน้อยโดยไม่ต้องเสียค่าใช้จ่ายเพิ่มเติม มันเป็นเรื่องเล็กน้อย
คนที่สองเข้าใจได้มากกว่านี้เล็กน้อย แต่เขาก็ยังผิดอยู่
นี่คือสิ่งที่C11 มาตรฐาน 6.5.5.p6พูดว่า:
หากความฉลาดทาง a / b แสดงออกได้การแสดงออก (a / b) * b + a% b จะเท่ากับ a; มิฉะนั้นพฤติกรรมของทั้ง a / b และ a% b นั้นไม่ได้ถูกกำหนดไว้
เชิงอรรถกล่าวว่า:
สิ่งนี้มักเรียกว่า "การตัดทอนเข้าหาศูนย์"
การตัดให้เป็นศูนย์หมายความว่าค่าสัมบูรณ์สำหรับa/b
เท่ากับค่าสัมบูรณ์(-a)/b
สำหรับทุกคนa
และb
ในทางกลับกันหมายความว่ารหัสของคุณนั้นสมบูรณ์แบบ
Modulo นั้นเป็นคณิตศาสตร์ที่ง่าย แต่อาจใช้งานง่าย
อย่างไรก็ตามคุณครูของคุณมีประเด็นที่คุณควรระวังเพราะความจริงที่ว่าคุณกำลังยกกำลังสองผลลัพธ์นั้นสำคัญมากที่นี่ การคำนวณa%b
ตามคำนิยามข้างต้นเป็นคณิตศาสตร์ง่าย ๆ แต่มันอาจขัดกับสัญชาตญาณของคุณ สำหรับการคูณและการหารผลลัพธ์จะเป็นค่าบวกหากตัวถูกดำเนินการมีเครื่องหมายเท่ากับ แต่เมื่อพูดถึง modulo ผลลัพธ์จะมีเครื่องหมายเหมือนกับตัวถูกดำเนินการแรก ตัวถูกดำเนินการตัวที่สองไม่ส่งผลกระทบต่อสัญลักษณ์เลย ตัวอย่างเช่น7%3==1
แต่(-7)%(-3)==(-1)
แต่
นี่เป็นตัวอย่างที่แสดงให้เห็น:
$ cat > main.c
#include <stdio.h>
void f(int a, int b)
{
printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}
int main(void)
{
int a=7, b=3;
f(a,b);
f(-a,b);
f(a,-b);
f(-a,-b);
}
$ gcc main.c -Wall -Wextra -pedantic -std=c99
$ ./a.out
a: 7 b: 3 a/b: 2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: 3 a/b: -2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: 7 b: -3 a/b: -2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: -3 a/b: 2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
ดังนั้นแดกดันครูของคุณพิสูจน์จุดของเขาโดยผิด
รหัสครูของคุณมีข้อบกพร่อง
ใช่มันเป็นจริง ถ้าอินพุตคือINT_MIN
AND สถาปัตยกรรมคือส่วนเติมเต็มสองและรูปแบบบิตโดยที่เครื่องหมายบิตคือ 1 และค่าบิตทั้งหมดเป็น 0 ไม่ใช่ค่าแทร็บn = n * (-1)
บนเส้น รหัสของคุณคือ - ถ้าดีกว่าเขาเล็กน้อย และเมื่อพิจารณาถึงการแนะนำบั๊กเล็ก ๆ โดยการทำให้โค้ดไม่จำเป็นต้องมีความซับซ้อนและมีค่าเป็นศูนย์อย่างแน่นอนฉันจะบอกว่าโค้ดของคุณนั้นดีกว่ามาก
ในคำอื่น ๆ ในการรวบรวมที่ INT_MIN = -32768 (แม้ว่าฟังก์ชั่นส่งผลให้ไม่สามารถรับการป้อนข้อมูลที่เป็น <-32768 หรือ> 32767) ที่ถูกต้องอินพุตที่ของ -32768 ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดเนื่องจากผลลัพธ์ของ - (- 32768i16) ไม่สามารถแสดงเป็นจำนวนเต็ม 16 บิต (ที่จริงแล้ว -32768 อาจจะไม่ก่อให้เกิดผลลัพธ์ที่ไม่ถูกต้องเพราะ - (- - 32768i16) มักจะประเมินที่ -32768i16 และโปรแกรมของคุณจะจัดการกับจำนวนลบอย่างถูกต้อง) (SHRT_MIN อาจเป็น -32768 หรือ -32767 ขึ้นอยู่กับคอมไพเลอร์)
แต่ครูของคุณระบุไว้อย่างชัดเจนว่าn
สามารถอยู่ในช่วง [-10 ^ 7; 10 ^ 7] จำนวนเต็ม 16 บิตน้อยเกินไป คุณจะต้องใช้ [อย่างน้อย] จำนวนเต็ม 32 บิต การใช้int
อาจทำให้โค้ดของเขาปลอดภัยยกเว้นว่าint
ไม่จำเป็นต้องเป็นจำนวนเต็ม 32 บิต ถ้าคุณคอมไพล์สำหรับสถาปัตยกรรมแบบ 16 บิตข้อมูลโค้ดทั้งคู่ของคุณจะมีข้อบกพร่อง แต่รหัสของคุณยังดีกว่ามากเพราะสถานการณ์นี้แนะนำข้อผิดพลาดที่INT_MIN
กล่าวถึงข้างต้นกับรุ่นของเขาอีกครั้ง เพื่อหลีกเลี่ยงปัญหานี้คุณสามารถเขียนlong
แทนint
ซึ่งเป็นจำนวนเต็ม 32 บิตในสถาปัตยกรรมใดสถาปัตยกรรมหนึ่ง A long
รับประกันว่าจะสามารถเก็บค่าใด ๆ ในช่วง [-2147483647; 2147483647] C11 มาตรฐาน 5.2.4.2.1 LONG_MIN
มักจะเป็น-2147483648
แต่ค่าสูงสุดที่อนุญาต (ใช่สูงสุดเป็นจำนวนลบ)LONG_MIN
2147483647
เป็น
ฉันจะเปลี่ยนแปลงรหัสของคุณอย่างไร
รหัสของคุณก็ดีเหมือนเดิมดังนั้นสิ่งเหล่านี้จึงไม่เป็นเรื่องร้องเรียน มันเป็นอย่างนั้นถ้าฉันจริง ๆ ต้องพูดอะไรเกี่ยวกับรหัสของคุณมีบางสิ่งเล็ก ๆ ที่สามารถทำให้ชัดเจนยิ่งขึ้น
- ชื่อของตัวแปรอาจจะดีขึ้นเล็กน้อย แต่มันเป็นฟังก์ชั่นสั้น ๆ ที่เข้าใจง่ายดังนั้นจึงไม่ใช่เรื่องใหญ่
- คุณสามารถเปลี่ยนสภาพจากการ
n
n!=0
ความหมายมันเทียบเท่า 100% แต่ทำให้ชัดเจนขึ้นเล็กน้อย
- ย้ายการประกาศของ
c
(ซึ่งฉันเปลี่ยนชื่อเป็นdigit
) ไปไว้ในขณะที่ลูปเนื่องจากใช้เฉพาะที่นั่นเท่านั้น
- เปลี่ยนประเภทอาร์กิวเมนต์เป็น
long
เพื่อให้แน่ใจว่าสามารถจัดการกับชุดอินพุตทั้งหมดได้
int sum_of_digits_squared(long n)
{
long sum = 0;
while (n != 0) {
int digit = n % 10;
sum += (digit * digit);
n /= 10;
}
return sum;
}
ที่จริงแล้วสิ่งนี้อาจทำให้เข้าใจผิดได้นิดหน่อยเพราะ - ดังกล่าวข้างต้น - ตัวแปรdigit
สามารถรับค่าลบได้ แต่ตัวเลขนั้นอยู่ในตัวของมันเองไม่ว่าจะเป็นบวกหรือลบ มีสองสามวิธีรอบ ๆ นี้ แต่นี่เป็น nitpicking จริง ๆ และฉันจะไม่สนใจรายละเอียดเล็ก ๆ น้อย ๆ โดยเฉพาะอย่างยิ่งฟังก์ชั่นที่แยกต่างหากสำหรับตัวเลขสุดท้ายคือมันไกลเกินไป นี่เป็นสิ่งหนึ่งที่รหัสครูของคุณแก้ไขได้จริง
- เปลี่ยน
sum += (digit * digit)
ไปsum += ((n%10)*(n%10))
และข้ามตัวแปรdigit
สมบูรณ์
- เปลี่ยนเครื่องหมาย
digit
ถ้าเป็นลบ แต่ฉันขอแนะนำอย่างยิ่งว่าอย่าให้รหัสซับซ้อนขึ้นเพียงเพื่อให้ชื่อตัวแปรเหมาะสมแล้ว นั่นคือกลิ่นรหัสที่แข็งแกร่งมาก
- สร้างฟังก์ชั่นแยกต่างหากที่แยกหลักสุดท้าย
int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }
สิ่งนี้มีประโยชน์หากคุณต้องการใช้ฟังก์ชั่นนั้นที่อื่น
- เพียงตั้งชื่อ
c
ตามที่คุณทำ ชื่อตัวแปรนั้นไม่ได้ให้ข้อมูลที่เป็นประโยชน์ แต่ในทางกลับกันก็ไม่ทำให้เข้าใจผิดเช่นกัน
แต่เพื่อความซื่อสัตย์ ณ จุดนี้คุณควรย้ายไปทำงานที่สำคัญกว่า :)
n = n * (-1)
เป็นวิธีที่ไร้สาระในการเขียนn = -n
; มีเพียงนักวิชาการเท่านั้นที่นึกถึง ปล่อยให้อยู่คนเดียวเพิ่มวงเล็บที่ซ้ำซ้อน