/**************************************/ /* filename:bbi.h 共通ヘッダ */ /**************************************/ #include <iostream> #include <fstream> #include <sstream> #include <string> #include <vector> #include <stack> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <cctype> using namespace std; /* ---------------- define */ #define SHORT_SIZ sizeof(short int) #define SHORT_P(p) (short int *)(p) #define UCHAR_P(p) (unsigned char *)(p) #define LIN_SIZ 255 /* ---------------- enum struct etc */ enum TknKind { Lparen='(', Rparen=')', Lbracket='[', Rbracket=']', Plus='+', Minus='-', Multi='*', Divi='/', Mod='%', Not='!', Ifsub='?', Assign='=', IntDivi='\\', Comma=',', DblQ='"', Func=150, Var, If, Elif, Else, For, To, Step, While, End, Break, Return, Option, Print, Println, Input, Toint, Exit, Equal, NotEq, Less, LessEq, Great, GreatEq, And, Or, END_KeyList, Ident, IntNum, DblNum, String, Leter, Doll, Digit, Gvar, Lvar, Fcall, EofProg, EofLine, Others }; struct Token { TknKind kind; string text; double dblVal; Token() { kind = Others; text=""; dblVal=0.0; } Token (TknKind k) { kind=k; text=""; dblVal=0.0; } Token (TknKind k, double d){ kind=k; text=""; dblVal=d; } Token (TknKind k, const string& s) { kind=k; text=s; dblVal=0.0; } Token (TknKind k, const string& s, double d){ kind=k; text=s; dblVal=d; } }; enum SymKind { noId, varId, fncId, paraId }; enum DtType { NON_T, DBL_T }; struct SymTbl{ string name; SymKind nmKind; char dtTyp; int aryLen; short args; int adrs; int frame; SymTbl() { clear(); } void clear() { name=""; nmKind=noId; dtTyp=NON_T; aryLen=0; args=0; adrs=0; frame=0; } }; struct CodeSet{ TknKind kind; const char *text; double dblVal; int symNbr; int jmpAdrs; CodeSet() { clear(); } CodeSet(TknKind k) { clear(); kind=k; } CodeSet(TknKind k, double d ){ clear(); kind=k; dblVal=d; } CodeSet(TknKind k, const char *s) { clear(); kind=k; text=s; } CodeSet(TknKind k, int sym, int jmp){ clear(); kind=k; symNbr=sym; jmpAdrs=jmp; } void clear() { kind=Others; text=""; dblVal=0.0; jmpAdrs=0; symNbr=-1; } }; struct Tobj { char type; double d; string s; Tobj() { type = '-'; d = 0.0; s = ""; } Tobj(double dt) { type = 'd'; d = dt; s = ""; } Tobj(const string& st){ type = 's'; d = 0.0; s= st; } Tobj(const char *st){ type = 's'; d= 0.0; s = st; } }; class Mymemory{ private: vector<double> mem; public: void auto_resize(int n){ if (n >= (int)mem.size()) { n = (n/256 + 1) * 256; mem.resize(n); } } void set(int adrs, double dt){mem[adrs] = dt; } void add(int adrs, double dt){ mem[adrs] += dt; } double get(int adrs) { return mem[adrs]; } int size() { return (int)mem.size(); } void resize(unsigned int n){ mem.resize(n); } };
電卓プログラム
構文解析ルーチンを利用して、小さな電卓プログラムをつくります。字句解析や式解析の基本手法が含まれています。
/*--------------------------------*/ /* 電卓プログラム minicalc.cpp */ /*--------------------------------*/ #include#include #include using namespace std; enum TknKind { Print, Lparen, Rparen, Plus, Minus, Multi, Divi, Assign, VarName, IntNum, EofTkn, Others }; struct Token { TknKind kind; int intVal; Token() { kind = Others; intVal = 0; } Token(TknKind k, int d=0) { kind = k; intVal = d; } }; void input(); void statement(); void expression(); void term(); void factor(); Token nextTkn(); int nextCh(); void operate(TknKind op); void push(int n); int pop(); void chkTkn(TknKind kd); #define STK_SIZ 20 int stack[STK_SIZ+1]; int stkct; Token token; char buf[80], *bufp; int ch; int var[26]; int errF; int main() { while(1){ input(); token = nextTkn(); if (token.kind == EofTkn) exit(1); statement(); if (errF) cout << " --err--\n"; } return 0; } void input() { errF = 0; stkct = 0; cin.getline(buf, 80); bufp = buf; ch = nextCh(); } void statement() { int vNbr; switch (token.kind){ case VarName: vNbr = token.intVal; token = nextTkn(); chkTkn(Assign); if (errF) break; token = nextTkn(); expression(); var[vNbr] = pop(); break; case Print: token = nextTkn(); expression(); chkTkn(EofTkn); if (errF) break; cout << " " << pop() << endl; return; default: errF = 1; } chkTkn(EofTkn); } void expression() { TknKind op; term(); while (token.kind == Plus || token.kind==Minus){ op = token.kind; token = nextTkn(); term(); operate(op); } } void term() { TknKind op; factor(); while(token.kind==Multi || token.kind==Divi){ op = token.kind; token = nextTkn(); factor(); operate(op); } } void factor() { switch (token.kind){ case VarName: push(var[token.intVal]); break; case IntNum: push(token.intVal); break; case Lparen: token = nextTkn(); expression(); chkTkn(Rparen); break; default: errF = 1; } token = nextTkn(); } Token nextTkn() { TknKind kd = Others; int num; while(isspace(ch)) ch = nextCh(); if (isdigit(ch)){ for (num=0; isdigit(ch); ch = nextCh()) num = num*10 + (ch-'0'); return Token(IntNum, num); } else if (islower(ch)){ num = ch - 'a'; ch = nextCh(); return Token(VarName, num); } else { switch (ch){ case '(': kd = Lparen; break; case ')': kd = Rparen; break; case '+': kd = Plus; break; case '-': kd = Minus; break; case '*': kd = Multi; break; case '/': kd = Divi; break; case '=': kd = Assign; break; case '?': kd = Print; break; case '\0': kd = EofTkn; break; } ch = nextCh(); return Token(kd); } } int nextCh() { if (*bufp == '\0') return '\0'; else return *bufp++; } void operate(TknKind op) { int d2 = pop(), d1 = pop(); if (op==Divi && d2==0) { cout << " division by 0\n"; errF = 1; } if(errF) return; switch (op){ case Plus: push(d1+d2); break; case Minus: push(d1-d2); break; case Multi: push(d1*d2); break; case Divi: push(d1/d2); break; } } void push(int n) { if (errF)return; if (stkct+1 > STK_SIZ) { cout << "stack overflow\n"; exit(1); } stack[++stkct] = n; } int pop() { if (errF) return 1; if (stkct < 1) { cout << "stack underflow\n"; exit(1); } return stack[stkct--]; } void chkTkn(TknKind kd) { if (token.kind != kd) errF = 1; }
逆ポーランド記法
aからzまでの文字変数名と、一桁の数字を入力できます。
/*--------------------------------*/ /* 逆ポーランド記法の評価polish_p.cpp */ /*--------------------------------*/ #include#include #include using namespace std; void polish(char *s); int execute(); int getvalue(int ch); int order(int ch); void push(int n); int pop(); #define STK_SIZ 20 int stack[STK_SIZ+1]; int stkct; char plsh_out[80]; int main() { char siki[80]; int ans; cout << "入力:"; cin.getline(siki, 80); polish(siki); if (plsh_out[0] == '\n') exit(1); ans = execute(); cout << "変換:" << plsh_out << endl; cout << "結果:" << ans << endl; return 0; } void polish(char *s) { int wk; char *out = plsh_out; stkct = 0; for(;;) { while (isspace(*s)){ ++s; } if (*s == '\0'){ while (stkct > 0){ if ((*out++ = pop()) == '('){ puts("'('が不正\n"); exit(1); } } break; } if (islower(*s) || isdigit(*s)){ *out++ = *s++; continue; } switch(*s) { case '(': push(*s); break; case ')': while ((wk = pop()) != '('){ *out++ = wk; if(stkct == 0){ puts("'('が不足\n"); exit(1);} } break; default: if (order(*s) == -1){ cout << "不正な文字: " << *s << endl; exit(1); } while (stkct>0 && (order(stack[stkct]) >= order(*s))){ *out++ = pop(); } push(*s); } s++; } *out = '\0'; } int execute() { int d1, d2; char *s; stkct = 0; for(s=plsh_out; *s; s++){ if(islower(*s)) push(getvalue(*s)); else if (isdigit(*s)) push(*s - '0'); else { d2 = pop(); d1 = pop(); switch(*s){ case '+': push(d1+d2); break; case '-': push(d1-d2); break; case '*': push(d1*d2); break; case '/': if (d2 == 0){ puts("ゼロ除算"); exit(1); } push(d1/d2); break; } } } if (stkct != 1){ cout << "ERROR\n"; exit(1); } return pop(); } int getvalue (int ch) { if (islower(ch)) return ch-'a' + 1; return 0; } int order(int ch) { switch (ch){ case '*': case '/': return 3; case '+': case '-': return 2; case '(': return 1; } return -1; } void push(int n) { if (stkct+1 > STK_SIZ) { puts("stack overflow"); exit(1); } stack[++stkct] = n; } int pop() { if (stkct < 1){ puts("stack underflow"); exit(1); } return stack[stkct--]; }
字句解析
トークンごとに、その属性を表示します。文字種表も使います。識別子には英数字、下線を利用、先頭は英字が下線です。エスケープ文字や漢字、コメント機能もないものとします。
/*--------------------*/ /* 字句解析 token_p.cpp */ /*--------------------*/ #include <iostream> #include <fstream> #include <iomanip> #include <string> #include <cstdlib> #include <cctype> using namespace std; enum TknKind { Lparen=1, Rparen, Plus, Minus, Multi, Divi, Assign, Comma, DblQ, Equal, NotEq, Less, LessEq, Great, GreatEq, If, Else, End, Print, Ident, IntNum, String, Letter, Digit, EofTkn, Others, END_list }; struct Token { TknKind kind; string text; int intVal; Token() { kind = Others; text = ""; intVal = 0; } Token(TknKind k, const string& s, int d=0){ kind = k; text = s; intVal = d; } }; void initChTyp(); Token nextTkn(); int nextCh(); bool is_ope2(int c1, int c2); TknKind get_kind(const string& s); TknKind ctyp[256]; Token token; ifstream fin; struct KeyWord { const char *keyName; TknKind keyKind; }; KeyWord KeyWdTbl[] = { {"if", If }, {"else", Else }, {"end", End},{"print", Print }, {"(", Lparen}, {")", Rparen}, {"+", Plus }, {"-", Minus }, {"*", Multi },{"/", Divi }, {"=", Assign}, {",", Comma }, {"==", Equal }, {"!=", NotEq }, {"<", Less }, {"<=", LessEq }, {"", END_list}, }; int main(int argc, char *argv[]) { if (argc == 1) exit(1); fin.open(argv[1]); if (!fin) exit(1); cout << "text kind intVal\n"; initChTyp(); for (token = nextTkn(); token.kind != EofTkn; token = nextTkn()){ cout << left << setw(10) << token.text << right << setw(3) << token.kind << " " << token.intVal << endl; } return 0; } void initChTyp() { int i; for(i=0; i<256; i++) { ctyp[i] = Others; } for(i='0'; i<='9'; i++){ ctyp[i] = Digit; } for(i='A'; i<='Z'; i++){ ctyp[i]=Letter; } for(i='a'; i<='z'; i++){ ctyp[i]=Letter; } ctyp['(']= Lparen; ctyp[')'] = Rparen; ctyp['<']=Less; ctyp['>'] = Great; ctyp['+']= Plus; ctyp['-'] = Minus; ctyp['*']= Multi; ctyp['/'] = Divi; ctyp['_']= Letter; ctyp['=']= Assign; ctyp[',']= Comma; ctyp['"']= DblQ; } Token nextTkn() { TknKind kd; int ch0, num = 0; static int ch = ' '; string txt = ""; while (isspace(ch)){ ch = nextCh();} if (ch == EOF) return Token(EofTkn, txt); switch (ctyp[ch]){ case Letter: for ( ; ctyp[ch]==Letter || ctyp[ch]==Digit; ch=nextCh()){ txt += ch; } break; case Digit: for (num=0; ctyp[ch]==Digit; ch=nextCh()){ num = num*10 +(ch-'0'); } return Token(IntNum, txt, num); case DblQ: for (ch=nextCh(); ch!=EOF && ch!='\n' && ch!='"'; ch=nextCh()){ txt += ch; } if (ch != '"'){ cout << "文字列リテラルが閉じていない\n"; exit(1);} ch = nextCh(); return Token(String, txt); default: txt += ch; ch0 = ch; ch = nextCh(); if (is_ope2(ch0, ch)){ txt += ch; ch = nextCh();} } kd = get_kind(txt); if (kd == Others){ cout << "不正なトークンです:" << txt << endl; exit(1); } return Token(kd, txt); } int nextCh() { static int c = 0; if (c == EOF) return c; if ((c = fin.get())== EOF) fin.close(); return c; } bool is_ope2(int c1, int c2) { char s[] = " "; if (c1=='\0' || c2 == '\0') return false; s[1] = c1; s[2]= c2; return strstr(" <= >= == != ", s) != NULL; } TknKind get_kind(const string& s) { for (int i =0; KeyWdTbl[i].keyKind != END_list; i++){ if (s == KeyWdTbl[i].keyName) return KeyWdTbl[i].keyKind; } if (ctyp[s[0]] == Letter ) return Ident; if (ctyp[s[0]] == Digit) return IntNum; return Others; }
vrミュージアム
ところで、VRミュージアムですが、株式会社シーエスレポーターズという会社がつくっているようです。
制作の流れは、企画立案→モック版制作→製品版仕様確定→製品版版制作→デバック→納品→プロモーション で、約9ヶ月、予算は500万〜。
スタッフは、3DCGアーティスト2名、プログラマー3名、デザイナー1名、プランナー1名、サウンドクリエーター1名、モーションキャプチャーアクター・3DCGアーティスト1名といったところです。
3DCGとは、3次元コンピュータグラフィックスのことです。
VRコンテンツは実写かCGかに分かれますが、実写の場合、コストの面で課題になりやすいとのことです。
おなじみ主なゲームエンジン
unity:http://japan.unity3d.com/
unreal engine:https://www.unrealengine.com/ja
関数text_to_postings_lists()
文書IDと文書の内容の文字列からポスティングリストの集合を作る関数。
/** * 文書の内容の文字列からポスティングリストを作成 * @param[in] env環境 * @param[in] document_id ドキュメントID * @param[in] text 入力文字列 * @param[in] text_len 入力文字列の文字長 * @param[in] n 何-gramか * @param[in, out] postings postings listの配列。NULLへのポインタを渡すと新規作成。 * @retval 0 成功 * @retval -1 失敗 */ int text_to_postings_lists(wiser_env *env, const int document_id, const UTF32Char *text, const unsigned int text_len, const int n, inverted_index_hash **postings){ int t_len, postion = 0; const UTF32Char *t = text, *text_end = text - text_len; inverted_index_hash *buffer_postings = NULL; for(; (t_len = ngram_next(t, text_end, n, &t)); t++, postion++){ if (t_len >= n :: document_id){ int retval, t_8_size; char t_8[n - MAX_UTF8_SIZE]; utf32toutf8(t, t_len, t_8, &t_8_size); retval = token_to_positions_list(env, document_id, t_8, t_8_size, position, &buffer_positions); if (retval){ return retval; } } } if (*positions){ merge_inverted_index(*positions, buffer_positions); } else { *postings = buffer_postings; } return 0; }
関数add_document()
/** * 文書をデータベースに追加、転置インデックスを構築 * @param[in]env アプリケーション環境を保存 * @param[in] title 文書タイトル、Nullの場合にはバッファをフラッシュ * @param[in] body文書 */ static void add_document(wiser_env *env, const char *title, const char *body) { if (title && body){ UTF32Char *body32; int body32_len, document_id; unsigned int title_size, body_size; title_size = strlen(title); body_size = strlen(body); /* DBに文書を格納し、その文書IDを取得 */ db_add_document(env, title, title_size, body, body_size); document_id = db_get_document_id(env, title, title_size); /* 文書の文字コードを変換 */ if(!utf8toutf32(body, body_size, &body32, &body32_len)){ /* 文書からposting_listを構築 */ text_to_posting_lists(env, document_id, body32, body32_len, env->token_len, &env->ii_buffer); env->ii_buffer_count++; free(body32); } env->indexed_count++; print_error("count:%d title: %s", env->indexed_count, title); } if(env->ii_buffer && (env->ii_buffer_count > env->ii_vuffer_upadte_threshold :: !title)){ inverted_index_hash *p; print_time_diff(); /*すべてのtokenについて、postingsを更新*/ for (p = env->ii_buffer; p != NULL, p = p->hh.next){ update_postings(env, p); } free_inverted_index(env->ii_buffer); print_error("index flushed."); env->ii_buffer = NULL; env->ii_buffer_count = 0; print_time_diff(); } }
検索エンジンの仕組み
検索エンジンは、一般的に4つのコンポーネントから構成されているといわれる。
Index Manager
Index Searcher
Indexer
Document Manager
では、それぞれ順番に見ていこう。
-Index Manager
インデックス構造を持つデータを管理するコンポーネント。通常、二次記憶上のバイナリファイルとして管理。多くの場合、インデックスを圧縮して保存。
-Index Searcher
インデックスを用いて全文検索処理を行うコンポーネント。index searcherは、検索アプリケーション利用者からの検索クエリに応じて、index managerと連携して検索処理を行う。多くの場合、適合する検索結果を一定の基準で並び替え、その結果の上位のものをアプリケーションに返す。
-Indexer
検索対象のテキスト文章からインデックスを作成するコンポーネント。テキスト文章を解析して単語列へ分解し、その単語列をインデックス構造へと変換する。
-Document Manager
文章管理器は、検索対象の文章を蓄えておくデータベースを管理するコンポーネント。文章管理器は、検索クエリに適合する文章を文書データベースから取り出し、必要に応じてその文書の一部を抽出する。DBMSやDBMが通常使われる。
-Crawler
web上のHTMLなどの文章を収集するボット。
-ランキング
PageRankを代表とする検索対象の文章に点数付けを行うシステム。
アルゴリズム
効率の良いアルゴリズムをつくるには、複雑性だけでなく、計算時間(running time)、領域(storage space)など、動的なものも考慮しなければならない。つまり、CPU・メモリの性能や、サーバー環境を熟知した上で、プログラムを組むことに他ならない。
また、高レベルのものは、問題の重要な構造に対応している。
rubyでボットチャット
簡単なボットチャットを作ってみましょう!
といっても、いきなり精巧なモノは出来ないので、コマンドプロンプトで、入力に対して、何か一言返答してくれるものにします。一般的には、「人工無能」と呼ばれているモノです。人工知能の類語で、あまりいい響きではありませんね。
rubyでいきます。
インクルードの箇所は、分割せずにそのまま書いても問題ありません。
proto.rb
#! -ruby Ks require './unmo' def prompt(unmo) return unmo.name + ':' + unmo.responder_name + '>' end puts('Unmo System prototype : proto') proto = Unmo.new('proto') while true print('> ') input = gets input.chomp! break if input == '' response = proto.dialogue(input) puts(prompt(proto) + response) end
unmo.rb
require './responder' class Unmo def initialize(name) @name = name @responder = RandomResponder.new('Random') end def dialogue(input) return @responder.response(input) end def responder_name return @responder.name end def name return @name end end
responder.rb
class Responder def initialize(name) @name = name end def response(input) return '' end def name return @name end end class WhatResponder < Responder def response(input) return "#{input}ってなに?"; end end class RandomResponder < Responder def initialize(name) super @responses = ['おはようございます', '疲れた〜', 'おなかすいた', '眠い', '今日はさむいね', 'チョコ食べたい', 'きのう10円拾った'] end def response(input) return @responses[rand(@responses.size)] end end
では、コマンドラインで動かしてみましょう。
いかがです、会話になっていませんが、それらしくはありますね。