Jul 12, 2014

Self Printing Program

หลายวันก่อนเปิดเจอโพสนี้ของคุณ @theppitak ซึ่งเป็นเรื่องของการเขียนโปรแกรมที่ต้องการผลลัพท์เป็นโปรแกรมที่กลับมาพิมพ์โปรแกรมตัวเอง

พูดง่ายๆ ว่าถ้าสมมติว่าโปรแกรมนั้นชื่อ 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+