2007-12-13 [長年日記]
λ. malloc-free な C 言語はチューリング完全
ku-ma-meさんが「malloc-free な C 言語はチューリング完全か」と書いていた。そこでku-ma-meさんも「再帰をうまく使えばできるのかな」と書いているけど、再帰を使って全部スタック上に確保してしまえば良さそうだ。 ヒープがないならスタックを使えばいいじゃない
Brainf*ck
とりあえず、メモリが有界でないBrainf*ckが実装できればチューリング完全だと思うので、実装してみた。
#include <stdio.h> struct cell { struct cell *prev, *next; unsigned char value; }; static void f(const char *pc, struct cell *p) { struct cell c = { .prev=p, .next=NULL, .value=0 }; if (p) p->next = &c; p = &c; while (1) switch (*pc++) { case '\0': return; case '>': if (p->next) p = p->next; else return f(pc, p); break; case '<': p = p->prev; break; case '+': p->value++; break; case '-': p->value--; break; case '.': putchar(p->value); break; case ',': p->value = getchar(); break; case '[': if (!p->value) { int lv=1; while (lv) switch (*pc++) { case '[': lv++; break; case ']': lv--; break; } } break; case ']': { int lv=1; pc-=2; while (lv) switch (*pc--) { case '[': lv--; break; case ']': lv++; break; } pc++; } break; } } void brainfuck(const char *prog){ f(prog, NULL); }
実行用のコード。
#include <glib.h> #include <stdio.h> #include <locale.h> int main(int argc, char **argv) { GError *err = NULL; gchar *prog; setlocale(LC_ALL, ""); g_file_get_contents(argv[1], &prog, NULL, &err); if (err) { puts(g_locale_from_utf8(err->message, -1, NULL, NULL, NULL)); return 1; } else { brainfuck(prog); return 0; } }
プログラムを読み込む処理は本質的ではないため、今回はプログラムの読み込み自体はmalloc-freeにはしなかった。というのも、読み込み処理をわざわざCで書かなくても、プログラムを既存の「brainf*ck自身で書かれたbrainf*ck処理系」に固定してしまえば済む話なので。
一般化?
ってか、つきつめて考えると、今のCならプログラムをCPS変換wして、以下のような関数を使えば良いか。しょーもないオチだけど。
void malloc_cps(size_t n, void (*k)(void*, void*), void *user_data) { unsigned char a[n]; k(a, user_data); }