Quine (Self Printing Program) ในภาษา C
หลายวันก่อนเปิดเจอโพสนี้ของคุณ @theppitak ที่ให้เขียนโปรแกรม quine ซึ่งเป็นโจทย์ปัญหาทางการเขียนโปรแกรมคอมพิวเตอร์ ที่ให้เราสร้างโค้ดที่เมื่อเรียกให้มันทำงานแล้ว ต้องได้ผลลัพธ์พิมพ์กลับออกมาเป็นโค้ดโปรแกรมต้นฉบับที่เราเขียนไป แน่นอนว่าการโกงโดยเปิดไฟล์โค้ดมาพิมพ์ไม่นับ
พูดง่ายๆ ว่าถ้าสมมติว่าโปรแกรมนั้นชื่อ self.c
การสั่งคำสั่ง diff
ครั้งสุดท้ายในโค้ดต่อไปนี้ต้องให้ค่าคืนมาเป็นว่า 2 ไฟล์มีเนื้อหาเหมือนกัน
$ gcc self.c
$ ./a.out > other.c
$ diff self.c other.c
เนื่องจากคุ้นๆ ว่าจะเคยเห็นคำถามแนวนี้ใน Ruby มาก่อนแล้ว ตอนแรกก็ไม่ได้คิดว่าจะลองทำหรอก แต่เท่าที่สังเกตแล้วจุดที่น่าจะเป็นปัญหาน่าจะมาจากเรื่อง escape string ซึ่งใน Python มันคงไม่ยุ่งยากเท่าไหร่ เพราะมี raw string และฟังก์ชั่น repr
ไว้คอยจัดการปัญหา escape string อยู่แล้ว
แต่คิดไปคิดมา เอ่อ แล้วถ้าเป็นใน C (ที่ต้นทางเค้าสงสัย) นี่มันจะทำยังไงหว่า? เลยลงมือเขียนเล่นๆ จนได้ออกมาดังนี้
void main() {
int i = 0, j = 0;
char* x = "void main() {\n\tint i = 0, j = 0;\n\tchar* x = \"%s\";\n\twhile (x[i]) {\n\t\tif (x[i++] == '%%' && x[i++] == 's') {\n\t\t\twhile (x[j]) {\n\t\t\t\tswitch (x[j++]) {\n\t\t\t\t\tcase '\\n': putchar('\\\\'); putchar('n'); break;\n\t\t\t\t\tcase '\\t': putchar('\\\\'); putchar('t'); break;\n\t\t\t\t\tcase '\\\"': putchar('\\\\'); putchar('\"'); break;\n\t\t\t\t\tcase '\\\\': putchar('\\\\'); putchar('\\\\'); break;\n\t\t\t\t\tdefault: putchar(x[j-1]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tputchar(x[i-1]);\n\t\t}\n\t}\n}\n";
while (x[i]) {
if (x[i++] == '%' && x[i++] == 's') {
while (x[j]) {
switch (x[j++]) {
case '\n': putchar('\\'); putchar('n'); break;
case '\t': putchar('\\'); putchar('t'); break;
case '\"': putchar('\\'); putchar('"'); break;
case '\\': putchar('\\'); putchar('\\'); break;
default: putchar(x[j-1]);
}
}
} else {
putchar(x[i-1]);
}
}
}
ไม่ได้จับ C มาหลายปี หมดพลังไปเยอะกว่าจะคิดออก 555+
Revision notes:
- April 12, 2016:
เพิ่งรู้ว่าโจทย์แนวนี้มีชื่อโดยเฉพาะเลยว่า quine ประทับใจจนต้องเปลี่ยนจั่วหัว blog ตอนนี้ 55555
author