to top page
2013-01-03
箱庭版「大菩薩峠」 by Awk
妙見の猿であります。
妙見の大木の大木の二人づれの巡礼は、たしかに巡礼の社の子の浪がします。
見るようにさわ立つ。
やれやれ頂上かい。
やれやれ頂上かい。
柳沢峠がござる。
見えて、何か知らん、海の上げられたように飛び出して来る波で岸へ着いたわい、ここの名物の社の前へ吹き上では従前の方を解いて跪まると、こうして小半時たる坂路の猿が開けてから後の来た小動物があるのであります。
右の紐を認めると、取合わず峠の前後、お爺さん、お爺さんというものは、一目を剥く廃道同様に人と見るようにさわ立つ。
そこを通れないことはないのです。
やれやれ頂上かい。

Awk スクリプトで生成したミニチュア版「大菩薩峠」。
「贋作・大菩薩峠」といえるような雄大なのを狙ったのだが、結果はこんなジオラマ風、箱庭風になった。
人工無能で使われる手法によったもので、考え方は次のとおり。
  1. 材料: 青空文庫から「大菩薩峠」の本文を一部借りてくる。
  2. 前処理: このテキストには括弧にはさまれた振り仮名がついているので、括弧ごと捨てる。その他多少の処理。
  3. 品詞分解: 漢字列(ひとつながりの漢字)を名詞とみなす。カタカナ列も同じ。ヒラガナ列は基本的に動詞とみなすが、列の先頭と末尾にある「は、が、の、に、へ、と、も、を」は助詞とみなす。(名詞、動詞、助詞の別は一連の処理には無関係)
  4. 文法表の作成: 分解された品詞を一つずつ文法表に収める。マルコフ連鎖アルゴリズム(わかってない)の初歩的な応用。
  5. 文章の生成: 文法表を見ながらセンテンス(句点で終わる)を1行ずつ出力し、所定の行数に達したら終了する。
ミニチュア版を生成する Awk スクリプト。
BEGIN {
    maxlines = 10
    PARENT = ""
}
{
    normalize()
    tokenize()
}
END {
    chainup()
    generate()
}

function normalize() {
    gsub(/(.+)/, "")
    gsub(/^「/, "")
    gsub(/「/, "")
    gsub(/」$/, "。")
}
function tokenize() {
    while (match($0, /[ぁ-ん]+|[ァ-ンー]+|[亜-龠]+|。|、/)) {
        token = substr($0, RSTART, RLENGTH)
        $0 = substr($0, RSTART+RLENGTH)
        if (token ~ /[ぁ-ん]/) {
            prefix = suffix = ""
            if (token ~ /^[はがのにへともを]/) {
                prefix = substr(token, 1, 1)
                token = substr(token, 2)
            }
            if (token ~ /[はがのにへともを]$/) {
                suffix = substr(token, RSTART)
                token = substr(token, 1, RSTART-1)
            }
            if (prefix) tokens[++tail] = prefix
            if (token) tokens[++tail] = token
            if (suffix) tokens[++tail] = suffix
        } else {
            tokens[++tail] = token
        }
    }
}
function chainup() {
    parent = PARENT
    for (i = 1; i <= tail; i++) {
        token = tokens[i]
        ++childcnt[parent]
        childlist[parent, childcnt[parent]] = token
        parent = token == "。" ? PARENT : token
    }
}
function generate() {
    while (maxlines--) {
        line = ""
        parent = PARENT
        while (childcnt[parent]) {
            i = int(childcnt[parent] * rand()) + 1
            line = line childlist[parent, i]
            parent = childlist[parent, i]
            if (parent == "。") break
        }
        print line
    }
}

これまでも人工無能にはお世話になってきた。
- 文筆用パワードスーツ
今までは Ruby を使っていたが、今回 Awk で書き直した。