bbi.h

/**************************************/
/*  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&#91;&#93;)
{
    if (argc == 1) exit(1);
    fin.open(argv&#91;1&#93;); 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&#91;i&#93; = Others; }
    for(i='0'; i<='9'; i++){ ctyp&#91;i&#93; = Digit; }
    for(i='A'; i<='Z'; i++){ ctyp&#91;i&#93;=Letter; }
    for(i='a'; i<='z'; i++){ ctyp&#91;i&#93;=Letter; }
    ctyp&#91;'('&#93;= Lparen; ctyp&#91;')'&#93; = Rparen;
    ctyp&#91;'<'&#93;=Less; ctyp&#91;'>'] = 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&#91;&#93; = "     ";
        if (c1=='\0' || c2 == '\0') return false;
        s&#91;1&#93; = c1; s&#91;2&#93;= 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が通常使われる。

%e6%a4%9c%e7%b4%a2%e3%82%a8%e3%83%b3%e3%82%b7%e3%82%99%e3%83%b3%e3%81%ae%e4%bb%95%e7%b5%84%e3%81%bf%e5%9b%b3%e8%a7%a3%e8%a9%b3%e7%b4%b0

-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

では、コマンドラインで動かしてみましょう。
いかがです、会話になっていませんが、それらしくはありますね。
ruby