robot
最新文章(10)
Mqskit 和其它相關工具
CPython 的 GC 二、三事
寫 Mecurial Extension 是件快樂的事!
Mozilla 台灣辨公室徵人啟事
關於 Apple 的兩項專利
core dump 之前的 frame
怎麼發出 beep 聲?
先承認你要找的是奴才吧!
程式碼要清的多乾淨?
FreeBSD 的 Thread-Local Storage 實作
首頁
新編
最新留言
Entries RSS
重要關鍵字(10)
coding (122)
Python (93)
FreeBSD (71)
WEB (61)
URL (48)
hardware (46)
javascript (36)
Linux (34)
blog (30)
C++ (16)
所有關鍵字
新增 URL
Programming with Virtual Machine
by thinker
2 Columns
關鍵字:
coding
在學校時,大概有不少人聽過這樣的$想法$ (OS) ,我們的軟體系統是透過定義一層又一層的 Virtual Machine 而建構起來的。我們程式,是在作業系統和 library 所造構出來的 VM 裡,使用 VM 所提供的指令 (API) ,建構程式的$應用$。 VM 能隱藏下層系統的複雜度,提供豐富的功能,而不必一一實作。程式將更簡單,連帶的,也更容易了解。例如,實作一個 media player ,我們需要實作各種不同的 codec ,參考了各種不同的技術和知識,系統複雜度相對的高。若以 gstreamer 為基礎,在其上實作一個 media player ,我們只需要 gstreamer (API) 的相關知識。gstreamer 建構的 VM ,使 media player 程式碼相對減少,直接參考的相關技術和知識也少很多。很明顯的, VM 的概念,能讓我們的事情更易於處理。我們不需去考量到 VM 的實作,只需知道 VM 提供什麼樣的功能。對容量有限的人腦而言,絕對一項思考利器。 我們平常在 $coding$ 時,除了使用現成 library 提供的 VM 之外,對於自己的 code ,也應該多加利用 VM 的概念,將問題切割成不同層次處理,以改善程式的可理解性/可讀性,同時也能提高程式碼的穩定度,減少 bug 的數量。 人腦的工作區域的容量很小,有所謂短期記憶只能記下七件事左右的限制。思考的過程,其實很依賴短期記憶,因而人腦其實較適於處理小問題,變數愈多的問題,大腦愈難以處理。問題愈大,大腦的錯誤率愈高。還好,人腦有抽象化的能力,透過抽象化,能將問題的細節隱藏,使大問題簡化成較高層次、變數較少的抽象問題。 因此,利用 VM 的概念,我們能將問題切割成不同的層面,從容不迫的分別處理,得到更好的思考品質。以下是一個分析 SQL command 的例子。 {{{#!cpp #define NEXT_TKN() { r = sql_token(&p, &q); } #define DUPTKN() {tkn = strndup(q, p - q); if(tkn == NULL) return ERR;} #define IS_LEXEME(x) ((*q == *(x) || *q == (*(x) - 32)) && \ strlen(x) == (p - q) && \ strncasecmp(q, x, p - q) == 0) #define IS_KEYWORD(kw) (r == SQL_ALPHA && \ IS_LEXME(kw)) #define IS_OP(o) (r == SQL_OP && *q == o) #define IS_KEYWORDS() (r == SQL_ALPHA && \ (IS_LEXEME("select") || \ IS_LEXEME("from") || \ IS_LEXEME("join") || \ IS_LEXEME("on") || \ IS_LEXEME("where") || \ IS_LEXEME("update") || \ IS_LEXEME("set") || \ IS_LEXEME("insert") || \ IS_LEXEME("into") || \ IS_LEXEME("values") || \ IS_LEXEME("group") || \ IS_LEXEME("by") || \ IS_LEXEME("order") || \ IS_LEXEME("asc") || \ IS_LEXEME("desc") || \ IS_LEXEME("limit"))) #define IS_VAR() (r == SQL_ALPHA && \ !IS_KEYWORDS() && \ !IS_OPS()) static int _check_insert(char **tbname, strnode **fields,const char *query) { const char *p, *q; char *tkn; int r; p = query; NEXT_TKN(); _ASSERT(IS_KEYWORD("insert")); NEXT_TKN(); _ASSERT(IS_KEYWORD("into")); NEXT_TKN(); _ASSERT(IS_VAR()); DUPTKN(); *tbname = tkn; NEXT_TKN(); if(IS_OP('(')) { while((r = sql_token(&p, &q)) > 0) { _ASSERT(IS_VAR()); DUPTKN(); add_strnode(fields, tkn); NEXT_TKN(); if(!IS_OP(',')) break; } _ASSERT(IS_OP(')')); NEXT_TKN(); } _ASSERT(IS_KEYWORD("values")); NEXT_TKN(); _ASSERT(IS_OP('(')); while((r = sql_token(&p, &q)) > 0) { _ASSERT(IS_LITERAL()); NEXT_TKN(); if(!IS_OP(',')) break; } _ASSERT(IS_OP(')')); NEXT_TKN(); _ASSERT(r == 0); return OK; } }}} 我們透過巨集建立一組指令,在這組指令上形成一個 VM ,透過 VM 的使用,我們大大的簡化 function 的邏輯,將 VM 實作的細節都隱藏起來。而 VM 又只實作一小部分的功能,其複雜度也較低,易於處理。於是,透過 VM 的使用,我們將一個大問題切割成數個易於處理的小問題,如此我們能處理的更快、更好,也有更高的品質。若不建立 VM ,我們程式的邏輯將複雜到難以處理,將花數倍的時間解決 bug 。例如,將上面的程式展開後如下 {{{#!cpp static int _check_insert(char **tbname, strnode **fields,const char *query) { const char *p, *q; char *tkn; int r; p = query; { r = sql_token(&p, &q); }; if(!((r == SQL_ALPHA && ((*q == *("insert") || *q == (*("insert") - 32)) && strlen("insert") == (p - q) && strncasecmp(q, "insert", p - q) == 0)))) return 1;; { r = sql_token(&p, &q); }; if(!((r == SQL_ALPHA && ((*q == *("into") || *q == (*("into") - 32)) && strlen("into") == (p - q) && strncasecmp(q, "into", p - q) == 0)))) return 1;; { r = sql_token(&p, &q); }; if(!((r == SQL_ALPHA && !(r == SQL_ALPHA && (((*q == *("select") || *q == (*("select") - 32)) && strlen("select") == (p - q) && strncasecmp(q, "select", p - q) == 0) || ((*q == *("from") || *q == (*("from") - 32)) && strlen("from") == (p - q) && strncasecmp(q, "from", p - q) == 0) || ((*q == *("join") || *q == (*("join") - 32)) && strlen("join") == (p - q) && strncasecmp(q, "join", p - q) == 0) || ((*q == *("on") || *q == (*("on") - 32)) && strlen("on") == (p - q) && strncasecmp(q, "on", p - q) == 0) || ((*q == *("where") || *q == (*("where") - 32)) && strlen("where") == (p - q) && strncasecmp(q, "where", p - q) == 0) || ((*q == *("update") || *q == (*("update") - 32)) && strlen("update") == (p - q) && strncasecmp(q, "update", p - q) == 0) || ((*q == *("set") || *q == (*("set") - 32)) && strlen("set") == (p - q) && strncasecmp(q, "set", p - q) == 0) || ((*q == *("insert") || *q == (*("insert") - 32)) && strlen("insert") == (p - q) && strncasecmp(q, "insert", p - q) == 0) || ((*q == *("into") || *q == (*("into") - 32)) && strlen("into") == (p - q) && strncasecmp(q, "into", p - q) == 0) || ((*q == *("values") || *q == (*("values") - 32)) && strlen("values") == (p - q) && strncasecmp(q, "values", p - q) == 0) || ((*q == *("group") || *q == (*("group") - 32)) && strlen("group") == (p - q) && strncasecmp(q, "group", p - q) == 0) || ((*q == *("by") || *q == (*("by") - 32)) && strlen("by") == (p - q) && strncasecmp(q, "by", p - q) == 0) || ((*q == *("order") || *q == (*("order") - 32)) && strlen("order") == (p - q) && strncasecmp(q, "order", p - q) == 0) || ((*q == *("asc") || *q == (*("asc") - 32)) && strlen("asc") == (p - q) && strncasecmp(q, "asc", p - q) == 0) || ((*q == *("desc") || *q == (*("desc") - 32)) && strlen("desc") == (p - q) && strncasecmp(q, "desc", p - q) == 0) || ((*q == *("limit") || *q == (*("limit") - 32)) && strlen("limit") == (p - q) && strncasecmp(q, "limit", p - q) == 0))) && !((r == SQL_OP && *q != '.' && *q != '*' && *q != '(' && *q != ')') || (r == SQL_ALPHA && (((*q == *("and") || *q == (*("and") - 32)) && strlen("and") == (p - q) && strncasecmp(q, "and", p - q) == 0) || ((*q == *("or") || *q == (*("or") - 32)) && strlen("or") == (p - q) && strncasecmp(q, "or", p - q) == 0) || ((*q == *("not") || *q == (*("not") - 32)) && strlen("not") == (p - q) && strncasecmp(q, "not", p - q) == 0))))))) return 1;; {tkn = strndup(q, p - q); if(tkn == ((void *)0)) return 1;}; *tbname = tkn; if((r == SQL_OP && *q == '(')) { while((r = sql_token(&p, &q)) > 0) { if(!((r == SQL_ALPHA && !(r == SQL_ALPHA && (((*q == *("select") || *q == (*("select") - 32)) && strlen("select") == (p - q) && strncasecmp(q, "select", p - q) == 0) || ((*q == *("from") || *q == (*("from") - 32)) && strlen("from") == (p - q) && strncasecmp(q, "from", p - q) == 0) || ((*q == *("join") || *q == (*("join") - 32)) && strlen("join") == (p - q) && strncasecmp(q, "join", p - q) == 0) || ((*q == *("on") || *q == (*("on") - 32)) && strlen("on") == (p - q) && strncasecmp(q, "on", p - q) == 0) || ((*q == *("where") || *q == (*("where") - 32)) && strlen("where") == (p - q) && strncasecmp(q, "where", p - q) == 0) || ((*q == *("update") || *q == (*("update") - 32)) && strlen("update") == (p - q) && strncasecmp(q, "update", p - q) == 0) || ((*q == *("set") || *q == (*("set") - 32)) && strlen("set") == (p - q) && strncasecmp(q, "set", p - q) == 0) || ((*q == *("insert") || *q == (*("insert") - 32)) && strlen("insert") == (p - q) && strncasecmp(q, "insert", p - q) == 0) || ((*q == *("into") || *q == (*("into") - 32)) && strlen("into") == (p - q) && strncasecmp(q, "into", p - q) == 0) || ((*q == *("values") || *q == (*("values") - 32)) && strlen("values") == (p - q) && strncasecmp(q, "values", p - q) == 0) || ((*q == *("group") || *q == (*("group") - 32)) && strlen("group") == (p - q) && strncasecmp(q, "group", p - q) == 0) || ((*q == *("by") || *q == (*("by") - 32)) && strlen("by") == (p - q) && strncasecmp(q, "by", p - q) == 0) || ((*q == *("order") || *q == (*("order") - 32)) && strlen("order") == (p - q) && strncasecmp(q, "order", p - q) == 0) || ((*q == *("asc") || *q == (*("asc") - 32)) && strlen("asc") == (p - q) && strncasecmp(q, "asc", p - q) == 0) || ((*q == *("desc") || *q == (*("desc") - 32)) && strlen("desc") == (p - q) && strncasecmp(q, "desc", p - q) == 0) || ((*q == *("limit") || *q == (*("limit") - 32)) && strlen("limit") == (p - q) && strncasecmp(q, "limit", p - q) == 0))) && !((r == SQL_OP && *q != '.' && *q != '*' && *q != '(' && *q != ')') || (r == SQL_ALPHA && (((*q == *("and") || *q == (*("and") - 32)) && strlen("and") == (p - q) && strncasecmp(q, "and", p - q) == 0) || ((*q == *("or") || *q == (*("or") - 32)) && strlen("or") == (p - q) && strncasecmp(q, "or", p - q) == 0) || ((*q == *("not") || *q == (*("not") - 32)) && strlen("not") == (p - q) && strncasecmp(q, "not", p - q) == 0))))))) return 1;; {tkn = strndup(q, p - q); if(tkn == ((void *)0)) return 1;}; add_strnode(fields, tkn); { r = sql_token(&p, &q); }; if(!(r == SQL_OP && *q == ',')) break; } if(!((r == SQL_OP && *q == ')'))) return 1;; { r = sql_token(&p, &q); }; } if(!((r == SQL_ALPHA && ((*q == *("values") || *q == (*("values") - 32)) && strlen("values") == (p - q) && strncasecmp(q, "values", p - q) == 0)))) return 1;; { r = sql_token(&p, &q); }; if(!((r == SQL_OP && *q == '('))) return 1;; while((r = sql_token(&p, &q)) > 0) { if(!((r == SQL_DIGIT || r == SQL_STR))) return 1;; { r = sql_token(&p, &q); }; if(!(r == SQL_OP && *q == ',')) break; } if(!((r == SQL_OP && *q == ')'))) return 1;; { r = sql_token(&p, &q); }; if(!(r == 0)) return 1;; return 0; } }}} 以上是使用 `gc -E' 展開後的結果。雖然是一樣的邏輯,但腦容量小如我者,實在難以處理。 事實上,以 VM 的形式實作,指令名稱能夠承載明確的語意。我們以 _ASSERT() 為例: {{{#!cpp if(r != SQL_OP) return ERR; }}} 和 {{{#!cpp _ASSERT(r == SQL_OP); }}} 後者的語意更為明確,前都必需先從 if 語法,在腦裡轉了一圈之後,才能了解是「檢查 r 必需為 SQL_OP,否則是一種錯誤」,而 _ASSERT() 則很明確的告訴讀 code 的人 「 r 必需為 SQL_OP」。另外, _ASSERT() 也隱藏了錯誤處理的邏輯,讓程式以更為線性的方式陳述。 以上的例子,我們透過巨集的使用,在 function level 建立 VM ,使的程式的邏輯能有清楚的劃分,讓整體更為清晰。我們應該在 programming 的各個階段,都盡可能的使用 VM 的觀念。隨時假設我們在設計一顆有加值功能的 CPU,讓上層$應用$能更輕鬆完成工作。即使在 $coding$ 一個 function 的層次,我們都能使用,以清晰我們的邏輯。
最後更新時間: 2008-06-21 19:37:21 CST |
引用
查詢:
COMMENTS: