平仮名フレーズ辞書を追加してみようかな(3)
先日来のエントリ
「2011-07-06Sekka* 平仮名フレーズ辞書を追加してみようかな(1)」
「2011-07-07Sekka* 平仮名フレーズ辞書を追加してみようかな(2)」
の続き。
実際にWebコーパスの6-gramのデータの中から、文末に出てくる定型フレーズを集めるスクリプトを書いてみた。 文末で、且つ、平仮名のみで構成されている形態素を連結した文字列をSekka用フレーズとした。 文末は </S> (文境界マーク) で判断した。
入力例
% が 加算 さ れ て 1152
% が 加算 さ れ ます 1463
% しか あり ませ ん </S> 1995
% だっ た そう です </S> 1554
% だっ た の が 、 1142
% だっ た の に対し 、 3496
% で 、 環境 問題 に 1898
% で あっ た が 、 2212
, と し て いる </S> 1007
, と 思い まし た </S> 1780
出力例
しかありません ;; 7 ;; ("!%" "しか" "あり" "ませ" "ん" "</S>" "1995")
だったそうです ;; 7 ;; ("!%" "だっ" "た" "そう" "です" "</S>" "1554")
としている ;; 5 ;; ("!," "と" "し" "て" "いる" "</S>" "1007")
ました ;; 3 ;; ("!," "と" "思い" "まし" "た" "</S>" "1780")
プログラムは[Nendo]で書いたが、非常に計算が重い。(処理系の問題) Gaucheでも動くポータブルなコードにすべきだったかも。まあ何回も動かすわけではないからいいか。
#!/bin/sh
:; #-*- mode: nendo; syntax: scheme -*-;;
:; exec /usr/local/bin/nendo $0 $*
(use srfi-1)
(use sekka.roman-lib)
(define (hiragana-filter words)
(define (hiras lst)
(cond
((null? lst)
lst)
((is-hiragana (car lst))
(cons
(car lst)
(hiras (cdr lst))))))
(let1 lst (cdr (reverse words))
(reverse (hiras lst))))
(define (include-slash-s? lst)
(any
(lambda (x y)
(and
(string=? "</S>" y)
(is-hiragana x)))
(cons "" lst)
lst))
(define (grep-last-phrase filename)
(with-open
filename
(lambda (f)
(for-each
(lambda (line)
(let* (*lst (to-list (line.chomp.split #/[ \t*+/))]
*freq (take-right lst 1)*
*words (drop-right lst 1)*)
(when (include-slash-s? words)
(let1 phrase (apply + (hiragana-filter words))
(printf "%s ;; %2d ;; %s\n"
phrase
phrase.size
(write-to-string lst))))))
(f.readlines.to_list)))))
(define (main argv)
(if (> 1 (length argv))
(error "hiragana_phrase.nnd requires *6gram web corpus file*")
(grep-last-phrase (car (to-list argv)))))
この処理のあと、「ー」が含まれるフレーズや、「っぁぃぅぇぉ」などで終わるフレーズは固い文章を書いている時に出てくると困るので、外している。 例えば、「どーも」とか「よろしくっ」とかが頻繁にサジェストされてると非常に困る。そういうくだけた表現は自分で故意に入力すればいい。 また、辞書の語彙として有意なものに限定するため 2文字から7文字までの長さのフレーズに絞り込んだ。
プログラムはこちら。
#!/bin/sh
:; #-*- mode: nendo; syntax: scheme -*-;;
:; exec /usr/local/bin/nendo $0 $*
(define (writing-phrase? str)
(not
(or
(rxmatch #/ー/ str)
(rxmatch #/*ぁぃぅぇぉゃゅょっー*$/ str))))
(define (writing-phrase-filter filename)
(with-open
filename
(lambda (f)
(for-each
(lambda (line)
(let* (*lst (to-list (line.chomp.split #/[ \t*+/))]
*word (car lst)*)
(when (and (<= 2 (word.size))
(<= (word.size) 7)
(writing-phrase? word))
(printf "%s //\n" word))))
(f.readlines.to_list)))))
(define (main argv)
(if (> 1 (length argv))
(error "writing_phrase_filter.nnd requires file as 'hiragana ;; ....' ")
(writing-phrase-filter (car (to-list argv)))))
ファイルから読みこんで1行ずつ処理するプログラムを書くことが多いので、もっと便利な関数を作りたくなってきた。 Gaucheに習って [Nendo] に port->string-list を作るかな。 あっ、そうか portの概念をどうする決めかねていて止まっているんだった。どうするかなぁ。 とりあえず これでお茶を濁してportとしてしまうかー。いいのかな。
class LispPort < File
end