ダジャレ判定機作ったよ
ダジャレが好きなのだ。
本来関連のない言葉同士を自由に結びつけ、語感のリズムで羽ばたいてシニフィエの宇宙を再構築する、そんなおやじギャグはとてもいとおしい。
だからぼくはダジャレのスキルを向上すべく日々研鑽しているのだ。
そういつもダジャレのことばかり考えている。PDCAサイクルを回して技術をみがいていく。
いやそれにはダジャレ度が定量的に計測可能でなくてはならない。
そうだ、ダジャレ度を測定するための機械を作ろう。←いまここ
そんなわけでダジャレの上手さを評価するウェブサービスを作りました。
もじった言葉ともとの言葉を比較して似ていると評価が高くなります。
ダジャレというより、いわゆるボキャブラですね。
ダジャレ判定機 (http://aikelab.net/dajare/)
文字列比較の仕組みはこんな感じです。
・MeCabで形態素解析して漢字を発音通りのよみがなに変換する
・大文字小文字などの表記ゆれを統一
・Ruby/Romkanでローマ字に変換
・レーベンシュタイン距離を計算
いったんローマ字にすることで、母音が一致しているような言葉の評価が高くなります。
ソース
#!/usr/bin/ruby $KCODE='UTF8' require 'jcode' require 'MeCab' require "kconv" require 'romkan' class Vocabla def initialize @mecab = MeCab::Tagger.new('-F%pS%f[8] -U%M -E\n') end def judge(s1, s2) rs1 = to_romaji(s1) rs2 = to_romaji(s2) dist = levenshtein(rs1, rs2) len = [utf8_length(rs1), utf8_length(rs2)].max score = 100 - (dist * 100 / len) end def level(score) case score when 0..29 "意味不明ダジャレ" when 30..64 "難解ダジャレ" when 65..79 "おやじギャグ" when 80..89 "秀逸ダジャレ" when 90..94 "絶妙ダジャレ" when 95..99 "超絶ダジャレ" when 100 "完全に一致" end end def to_romaji(s) s ||= "" ss = @mecab.parse(s) # 漢字->カタカナ変換 ss.tr!('a-zA-Z','a-zA-Z') # 英数文字を半角に変換 ss.downcase! # 英数文字を小文字に変換 ss.tr!('ァ-ン','ぁ-ん') # カタカナ->ひらがな変換 ss.gsub!('を','お') # 'を'->'お'変換 ss.gsub!(/[ ]/,'') # 空白文字の削除 ss.gsub!(/[・!、。]/,'') # 記号文字の削除 ss = ss.to_roma.to_kunrei # ローマ字(訓令式)変換 return ss end # levenshtein function for utf8 multibyte character (ruby 1.8) # based on # http://subtech.g.hatena.ne.jp/cho45/20091008/1254934083 def levenshtein(a, b) a_length = utf8_length(a) b_length = utf8_length(b) case when a.empty? b_length when b.empty? a_length else d = Array.new(a_length + 1) { |s| Array.new(b_length + 1, 0) } (0..a_length).each do |i| d[i][0] = i end (0..b_length).each do |j| d[0][j] = j end (1..a_length).each do |i| (1..b_length).each do |j| cost = (utf8_substring(a, i - 1) == utf8_substring(b, j - 1)) ? 0 : 1 d[i][j] = [ d[i-1][j ] + 1, d[i ][j-1] + 1, d[i-1][j-1] + cost ].min end end d[a_length][b_length] end end def utf8_length(s) s.split(//u).length end def utf8_substring(s, n) s.split(//u)[n] end end