Ruby/Programming/川柳ジェネレータちかぽん

Last-modified: 2008-04-13 (日) 00:08:36

2008.04.12 掲載

過去の経緯

  • 2008.02.16 「辞書」を増強
  • 2008.初春 初版

ブログペットというヤツがあって(暫く前には「こうさぎ」と称してた)、人のブログに住みつき人のブログを盗み読んでは時時一句捻る。

真似してやろうと思った。

どうやってやっつけるか

ご本家の方はけっこう手の込んだことをするみたいで、単にブログのテキストを切り刻んで五七五に仕立て直すだけじゃなく、ちゃと「句」っぽく感じられるように語句の選択や配置を工夫している。ように見える。*1

こちらはウェブに棲息するにはまだ進化が足りないんで、人のブログといった餌がない。「餌」というか辞書は自前で蓄えるしかない。次バージョンでは辞書のファイル化が実装される予定。

問題は、いかにして「川柳」っぽく見せかけるかだ。ハナモゲラ和歌ではひらがなをランダムに並べればよかったけれど、「川柳っぽくする」には違うアイデアが必要だ。

数年間折に触れて考えていたもののいい考えが浮かばなかった。やはり本気を出すと違いますね。真剣に考え始めてほどなく、「基本語句(主に名詞)に、助詞や動詞句をくっつける」という手を思いついた。それぞれを辞書の形で保持し、ランダムに選びだせばいい。

これで基本型はできたも同然。後は細部の微調整と「辞書」の充実だ。

プログラムの構造と実行

やってみると、基本語句の長さに合わせて後置句(助詞や動詞をこう総称している)の長さを変えなければいけない。基本語句の長さを知るために、語句の読み仮名を辞書gokuに持たせることにした。行きがかり上、語句がキーで読みが値という、ヘンな形式になっている。辞書を読みで整列するなら読みをキーにするべきだろうけど、整列する必要のないプログラムだし。

語句の長さは川柳の流儀に準じる。

  1. 基本は音節
  2. 拗音は一音に数えない
  3. 長音は一音扱い

それで音字数を数える時には読みから拗音を取り除いている。メソッドonjisuuがそれ。*2

上の句メソッドkami5で、語句の長さが4なら後置句は長さ1のものを選ぶ。3なら長さ2、2なら3、を選ぶ。後置句を選ぶのはそれぞれkouti1, kouti2, kouti3, kouti4が担当する。

中の句はnaka7、下の句はsimo5が同様に執り行うが、simo5はkami5を読んで済ませている。切れ字の処理なんかするんなら別に実装することになるんだろう。naka7では、語句の長さが2の時は、長さ4の後置句では足りないんで、その前に長さ1の後置句(助詞ですよね)をつける。

ちなみに辞書gokuは上・中・下の句で共用。長さに上限も下限も設けない。長さ1の語句は、たぶん辞書に入れない。長い方は入れておいてもかまわない。たとえば上の句で長さ5以上の語句を選んだり、中の句で長さ7以上のものを選んだりすることがあってもスルーしている。時には字余りも許すという塩梅である。*3

最後の実行部分では、コマンドライン引数で詠む句数を指定できるようにしている。

プログラム的にはこれだけのもので、後は辞書次第で案外それらしいものが「詠める」。お楽しみください。

プログラム

#! /usr/local/bin/ruby -Ks
$KCODE = "SJIS"
require 'jcode'
$goku = {
  "自分史" => "じぶんし",
  "自叙伝" => "じじょでん",
  "自画像" => "じがぞう",
  "私語" => "しご",
  "私小説" => "ししょうせつ",
  "縄のれん" => "なわのれん",
  "コップ酒" => "こっぷざけ",
# ...(省略)...
  "冬の海" => "ふゆのうみ",
  "夏の海" => "なつのうみ",
  "泪川" => "なみだがわ",
  "桜" => "さくら",
  "夏休み" => "なつやすみ",
  "除夜の鐘" => "じょやのかね",
  "残り火" => "のこりび",
  "粉骨砕身" => "ふんこつさいしん",
  "臥薪嘗胆" => "がしんしょうたん",
  "吹溜り" => "ふきだまり",
  "春の嵐" => "はるのあらし",
  "谺" => "こだま",
  "逃避行" => "とうひこう",
# ... 以下、好きな語句を登録 ...
}
def pickup
  $goku.keys[rand($goku.keys.size)]
end
def kouti1
  koutiku = [
  "が", "で", "と", "に", "ね", "の", "は", "も", "へ", "や", "を",
  ]
  koutiku[rand(koutiku.size)]
end
def kouti2
  koutiku = [
  "から", "こそ", "だね", "だよ", "でも", "なの", "なら", "なり",
  "には", "にも",
  "へと", "へは",
  "今", "立つ", "する",
  "降る", "降れ",
  ]
  koutiku[rand(koutiku.size)]
end
def kouti3
  koutiku = [
  "けれど",
  "だから", "だけど", "だけに", "だけの", "だよね", "でもね",
  "なのに", "なのね", "ならば", "なりよ", "なれど",
  "少し", "満ちる", "満ちて", "くじる", "広がる",
  "濡れる", "揺れる", "走る", "溶ける", "光る", "伸びる", "縮む",
  "走れ", "光れ", "光れ", "伸びろ", "縮め",
  "降れば",
  ]
  koutiku[rand(koutiku.size)]
end
def kouti4
  koutiku = [
  "名だたる", "見つけた", "見つけて", "溶け出す",
  "華やぐ", "聞こえた", "聞こえる", "去りゆく",
  "育む", "彩る", "輝く", "輝け", "高鳴る",
  "燃え出す", "燃え立つ", "波打つ",
  ]
  koutiku[rand(koutiku.size)]
end
def onjisuu(s)
  momo = $goku[s].gsub(/[ゃゅょ]/, "")
  return momo.length / 2
end
def kami5
  s = pickup
  case onjisuu(s)
  when 4
    s + kouti1
  when 3
    s + kouti2
  when 2
    s + kouti3
  else
    s
  end
end
def naka7
  s = pickup
  case onjisuu(s)
  when 6
    s + kouti1
  when 5
    s + kouti2
  when 4
    s + kouti3
  when 3
    s + kouti4
  when 2
    s + kouti1 + kouti4
  else
    s
  end
end
def simo5
  kami5
end
if __FILE__ == $0 then
  rep = ARGV[0].to_i if ARGV.size > 0
  rep = 1 if rep == nil
  rep.times do
    print kami5, " ", naka7, " ", simo5, "\n"
  end
end

垢光れ 指人形も 初詣

万華鏡 夏も溶け出す 無為徒食

根深汁 春の嵐を 私小説

血煙を 夏の海なり ウイスキー

自分史や ビール輝け 粉骨砕身

ここを参照しているページ

#related: relatedプラグインは廃止されました。


*1 買い被りかなあ。だとしたら、「手の込んだ工夫をしていると思わせる程度にうまいことをやっている」わけで、やはり感嘆に値すると思うよ。
*2 「ぃぅぉ」が削除対象にないのはバグですね。あと、長さを測るのにlengthの値を2で割っているのもどうかなあ。jlengthを使うべきところだろう。
*3 辞書には川柳本に載っている語句もいくつか取り入れた。