webAi.JpMrkv - ソースコード

説明

Web AI」は、クロノス・クラウンの柳井政和が開発しているJavaScirptのライブラリです。「Web AI」を使うと、Web上からデータを取得して構築するWebページを、とても簡単に構築することができます。

「Web AI」は、Webからの検索結果やフィードの取得、短縮URLへのURLの変換、日本語文章からのキーワードや文章抽出、日本語文章のマルコフ連鎖、クエリーの管理などの機能を備えています。

このページでは、この「Web AI」のファイルの1つである「crocro.webAi.JpMrkv.js」のソースコードを掲載します。

「crocro.webAi.JpMrkv.js」

「crocro.webAi.JpMrkv.js」には、日本語の文章から、マルコフ連鎖を使って、シャッフルした新しい文章を作成する機能がまとめられています。このファイルには「crocro.webAi.JpMrkv」オブジェクトが収録されています。

使い方は、「Web AI」の各サンプルを参考にしてください。

ソースコード

以下、「crocro.webAi.JpMrkv.js」のソースコードです。

    // crocro.webAiの初期化
    if (! crocro) var crocro = {};
    if (! crocro.webAi) crocro.webAi = {};

    /**
     *    @variable    crocro.webAi.JpMrkv(arg)
     *    @title    日本語マルコフ連鎖
     *    @description
     *
     *        日本語の文章から、マルコフ連鎖を行うオブジェクト。
     *        newで初期化してから利用する。
     *
     *        引数は連想配列で設定する。例){~: "~", …}
     *
     *    @param    arg.ndMax        文章の最大ノード。
     *    @param    arg.chain        接頭文字数(デフォルトは1。2以上の場合は、似たような文章が大量に必要)。
     *    @param    arg.bsSntncLen    基本文章長。
     */
    crocro.webAi.JpMrkv = function(arg) {
        // ユーザー変数
        /**
         *    @variable    JpMrkv.ndMax
         *    @title    文章の最大ノード
         *    @description
         *        生成する文章の長さを規定する。長さの単位はノード。何ノードまでの連結を許すか。
         *
         *        デフォルトは「300」。
         */
        /**
         *    @variable    JpMrkv.chain
         *    @title    接頭文字数
         *    @description
         *        ノード連結単位。1の場合は、前後の繋がりだけを見る。
         *        2以上の場合は、2つ前、3つ前の繋がりも見て確認する。
         *
         *        デフォルトは「1」。「2」以上の場合は、似たような文章が大量に必要になる。
         */
        /**
         *    @variable    JpMrkv.bsSntncLen
         *    @title    基本文章長
         *    @description
         *        だいたい、この文字数になるように連結を目指す値。
         *
         *        デフォルトは「25」。
         */
        this.ndMax = 300;            // 文章の最大ノード
        this.chain = 1;                // 接頭文字数(デフォルトは1。2以上の場合は、似たような文章が大量に必要)
        this.bsSntncLen = 25;        // 基本文章長

        // ユーザー変数の設定
        if (arg) {
            if ("ndMax"      in arg) this.ndMax = arg.ndMax;
            if ("chain"      in arg) this.chain = arg.chain;
            if ("bsSntncLen" in arg) this.bsSntncLen = arg.bsSntncLen;
        }

        // 内部利用変数
        this.dic = {};                // 辞書
        this.dbgStr = "";            // デバッグ用文字列

        // 日本語分割用正規表現
        /**
         *    @variable    JpMrkv.bsRe
         *    @title    日本語分割用正規表現
         *    @description
         *        日本語の文章をノードに分解するための正規表現。
         *        この値を差し替えると、違う分割でマルコフ連鎖を行える。
         */
        this.bsRe = new RegExp(
              ""
            + "「.+?」|『.+?』|(.+?)|\(.+?\)|"
            + "[ァ-ヶ・=ー0-90-9一-龠A-Za-z0-9\w\-]{2,}|"
            + "[一-龠々〆ヵヶ]+、{0,1}|[ぁ-ん]+、{0,1}|[ァ-ヴー]+、{0,1}|"
            + "[a-zA-Z0-9]+、{0,1}|[a-zA-Z0-9]+、{0,1}|"
            + "[、。!!??()()「」『』]{1}",
            "g"
        );

        // キー内の位置
        KEY_STR = 0;    // 文字列
        KEY_POS = 1;    // 位置
        KEY_LEN = 2;    // 全体サイズ

        /*
         *--------------------------------------------------
         */

        /**
         *    @access    private
         *    @variable    JpMrkv.Rndm(dbgSeed_)
         *    @title    ランダム
         *    @description
         *
         *        ランダムな値を取得するためのオブジェクト。
         *        デバッグ・シードでのランダムは、デバッグ用途なので精度は悪い。
         *
         *    @param    dbgSeed_    デバッグ・シードを利用するか。
         *                        指定なしか-1の時は通常のランダム。
         *    @return    ランダムな値。
         */
        function Rndm(dbgSeed_) {
            var dbgSeed = dbgSeed_ || -1;
            var nowKey = 2*3*5*7*11*13*17-1 - dbgSeed;

            /**
             *    @access    private
             *    @variable    JpMrkv.Rndm.nextInt(strt, max)
             *    @title    数値取得
             *    @description
             *
             *        ランダムな値を取得する。
             *
             *    @param    strt    開始数値。
             *    @param    strt    最大数値(この値未満となる)。
             *    @return    ランダムな値。
             */
            this.nextInt = function(strt, max) {
                var res = 0;
                var rng = max - strt;
                if (dbgSeed == -1) {
                    res = strt + Math.floor(Math.random() * rng);    // 通常
                } else {
                    nowKey = (nowKey * 1103515245 + 12345);
                    if (nowKey < 0) {
                        nowKey /= 2;
                        nowKey *= -1;
                    }
                    nowKey = nowKey & 2147483647;
                    res = strt + nowKey % rng;    // デバッグ・シード
                }
                return res;
            }
        }

        /*
         *--------------------------------------------------
         */

        /**
         *    @variable    JpMrkv.reset(arg)
         *    @title    リセット
         *    @description
         *
         *        内容をリセットしてメモリを解放する。
         *        設定はそのまま維持する。
         *
         *    @return    なし。
         */
        this.reset = function()  {
            this.dic = {};                // 辞書
            this.dbgStr = "";            // デバッグ用文字列
        }

        /**
         *    @variable    JpMrkv.setSntncArr(srcArr)
         *    @title    文章の登録
         *    @description
         *
         *        文章の配列を引数として、マルコフ連鎖用の文章を登録する。
         *
         *    @param    srcArr        元になる文章配列を指定。
         *    @return    なし。
         */
        this.setSntncArr = function(srcArr)  {
            for (var i = 0; i < srcArr.length; i ++) {
                this.addSntnc(srcArr[i]);
            }
        }

        /**
         *    @variable    JpMrkv.addSntnc(srcStr)
         *    @title    文章の登録
         *    @description
         *
         *        通常は内部で使用する、文章追加メソッド。
         *
         *    @param    srcStrr        元になる文章を指定。
         *    @return    なし。
         */
        this.addSntnc = function(srcStr)  {
            var strArr = srcStr.match(this.bsRe);
            if (! strArr) return;

            // 追加可能文章か判定
            if (! srcStr.match("[るただい]$")) return;

            // デバッグ用
            //this.dbgStr += srcStr + "<BR>[" + strArr + "]<BR><BR>";

            strArr.unshift("_START_");    // 先頭

            // n接頭文字1接尾文字
            var n = this.chain;        // 接頭文字の数

            // 末尾の追加
            for (var i = 0; i < n; i ++) {strArr.push("_END_");}

            for (var i = 0; i < strArr.length; i ++) {
                if (strArr[i] == "0") {strArr[i] = "0";}    // ハッシュに使えない添え字対策
            }

            var strArrLen = strArr.length;
            var strArrPos = 0;

            while (strArr[n]) {            // 文字列配列が残っている間実行
                if (! this.dic[strArr[0]]) this.dic[strArr[0]] = new Array();

                var pntr = this.dic[strArr[0]];        // 配列のポインタ

                if (n > 1) {
                    for (var i = 1; i <= n; i ++) {
                        // キー配列の追加
                        if (! pntr["_KEY_"]) pntr["_KEY_"] = [];    // キー配列の作製
                        var keyArr = new Array();
                        keyArr[KEY_STR] = strArr[i];        // 文字列
                        keyArr[KEY_POS] = strArrPos;        // 位置
                        keyArr[KEY_LEN] = strArrLen;        // 全体サイズ
                        pntr["_KEY_"].push(keyArr);
                        strArrPos ++;

                        // ツリーの追加

                        if (! pntr[strArr[i]]) pntr[strArr[i]] = [];    // ツリーの作製
                        pntr[strArr[i]].push(strArr[i + 1]);

                        pntr = pntr[strArr[i]];
                    }
                } else {
                    pntr.push(strArr[1]);
                }

                strArr.shift();            // 先頭を削除
            }
        };

        /*
         *--------------------------------------------------
         */

        /**
         *    @variable    JpMrkv.getDumpDic()
         *    @title    辞書のダンプ
         *    @description
         *
         *        辞書の構造を「<ul><li>~</li></ul>」のHTML文字列として取得する。
         *
         *    @return    辞書の構造をツリーにしたHTML文字列。
         */
        this.getDumpDic = function() {
            var resStr = getDump(this.dic);
            return resStr;
        }

        /**
         *    @access    private
         *    @variable    JpMrkv.getDump(obj)
         *    @title    辞書のダンプ(内部再帰)
         *    @description
         *        辞書のダンプで使用する再帰関数。
         *    @param    obj        ノードのオブジェクト。
         *    @return    辞書の構造をツリーにしたHTML文字列。
         */
        function getDump(obj) {
            var resStr = '<UL style="margin-left: 1em;">';
            for (var prop in obj){
                resStr += "<LI>" + prop + "=" + obj[prop] + "</LI>";
                resStr += this.getDump(obj[prop]);
            }
            resStr += "</UL>";
            return resStr;
        }

        /*
         *--------------------------------------------------
         */

        /**
         *    @variable    JpMrkv.getSntnc(arg)
         *    @title    マルコフ連鎖の実行
         *    @description
         *
         *        ランダム・キーと、開始に使用したい文字列で、マルコフ連鎖を実行する。
         *
         *    @param    arg.rndKey        ランダムのデバッグ・シード・キー。-1の場合は通常のランダム。
         *    @param    arg.strtStr        開始に使用する文字列。「""」の場合は先頭文字からランダムで決定。
         *    @return    作成した文章。
         */
        this.getSntnc = function(arg) {
            // 変数の初期化
            var rndKey = -1;
            var strtStr_ = "";

            if (arg) {
                if ("rndKey"  in arg) rndKey   = arg.rndKey;
                if ("strtStr" in arg) strtStr_ = arg.strtStr;
            }


            // 変数の初期化
            var sntnc = '';
            var sntCnt = 0;                    // センテンスの長さ
            var rndm = new Rndm(rndKey);    // シード付きランダム
            var strtStr = '_START_';
            if (strtStr_ != "" && this.dic[strtStr]) {
                strtStr = strtStr_;
                sntnc += strtStr_;
            }

            // n接頭文字1接尾文字
            if (this.dic[strtStr]) {
                // 接頭文字の数
                var n = this.chain;
                var w = new Array();
                var pntr;

                // 最初の連鎖の初期化
                pntr = this.dic[strtStr];
                if (pntr["_KEY_"]) {
                    w[0] = getRndFrmKeyPntr(pntr, rndm, 0);
                } else {
                    w[0] = getRndFrmPntr(pntr, rndm);
                }

                sntnc += w[0];
                sntCnt ++;

                for (var j = 1; j < n; j ++) {
                    pntr = pntr[w[j - 1]];
                    if (pntr["_KEY_"]) {
                        w[j] = getRndFrmKeyPntr(pntr, rndm, j);
                    } else {
                        w[j] = getRndFrmPntr(pntr, rndm);
                    }
                }

                // 連鎖で結合
                for (var i = 0; i <= this.ndMax; i ++) {
                    // 文章の連結
                    if (w[1] == "_END_") break;        // 末尾なので終了
                    if (w[1]) {
                        sntnc += w[1];        // 1接頭文字対策
                        sntCnt ++;
                    }

                    pntr = this.dic[w[0]];

                    if (n > 1) {
                        var j = 1;
                        for (; j < n; j ++) {
                            pntr = pntr[w[j]];
                        }
                        if (pntr["_KEY_"]) {
                            w[j] = getRndFrmKeyPntr(pntr, rndm, sntCnt + j);
                        } else {
                            w[j] = getRndFrmPntr(pntr, rndm);
                        }
                    } else {
                        w[1] = getRndFrmPntr(pntr, rndm);
                    }

                    // 次の位置にスライド
                    for (var j = 0; j < n; j ++) {
                        w[j] = w[j + 1];
                    }
                }
            }

            // 短すぎるセンテンスは無効
            if (sntCnt <= 2) return "";

            // 戻り値を戻して終了
            return sntnc;
        };

        /**
         *    @access    private
         *    @variable    getRndFrmPntr(pntr, rndm)
         *    @title    ポインタからランダム取得
         *    @description
         *
         *        ポインタから、連鎖のツリーをランダムで取得。
         *
         *    @param    pntr    ランダムのデバッグ・シード・キー。-1の場合は通常のランダム。
         *    @param    rndm    開始に使用する文字列。「""」の場合は先頭文字からランダムで決定。
         *    @return    文字列。
         */
        function getRndFrmPntr(pntr, rndm) {
            var w = pntr[rndm.nextInt(0, pntr.length)];
            return w;
        }

        /**
         *    @access    private
         *    @variable    JpMrkv.getRndFrmKeyPntr(pntr, rndm, sntPos)
         *    @title    キー・ポインタからランダム取得
         *    @description
         *
         *        ポインタから、連鎖のツリーをランダムで取得。
         *        文章中の、どの辺りにある単語かで、優先順位を変える。
         *
         *    @param    pntr    ランダムのデバッグ・シード・キー。-1の場合は通常のランダム。
         *    @param    rndm    開始に使用する文字列。「""」の場合は先頭文字からランダムで決定。
         *    @param    sntPos    文章内での位置。
         *    @return    文字列。
         */
        function getRndFrmKeyPntr(pntr, rndm, sntPos) {
            var keyArr = pntr["_KEY_"];
            var newArr = new Array();
            for (var i = 0; i < keyArr.length; i ++) {
                // 文章中の、どの辺りにある単語かで、優先順位を変える。
                var bsPer = Math.min(sntPos / this.bsSntncLen * 100, 100);
                var dtPer = Math.min(keyArr[i][KEY_POS] / keyArr[i][KEY_LEN] * 100, 100);
                var dst = (dtPer - bsPer) / 10;
                if (dst < 0) dst = dst * -1;
                var len = Math.max(1, 10 - dst);    // 大きい方を取得

                for (var j = 0; j < len; j ++) {
                    newArr.push(keyArr[i][KEY_STR]);
                }
            }
            var w = newArr[rndm.nextInt(0, newArr.length)];
            return w;
        }
    };
Cronus Crown(クロノス・クラウン)のトップページに戻る
(c)2002-2017 Cronus Crown (c)1997-2017 Masakazu Yanai
このWebPageに関するご意見・お問い合わせは
サイト情報 弊社への連絡
までお願いします。