challenge ナイツ関数(ボケの方)

入力した文章のところどころを言い間違えて出力する関数を実装してください。
(ナベアツ算を見てて思いつきました)

入出力の方法は標準入出力や引数・戻り値など、扱いやすい方法でかまいません。

文字単位でランダムに間違えても面白くないので、単語のリストから似た単語の候補を探すようにしてください。英単語でもOKです。単語のリストは参考ページからダウンロードしたものを加工して利用すると良いと思います。(4000個あります)

結果がつまらなくても構いませんが、面白いボケをうむ工夫があると良いです。
※人が考えたボケは求めてませんよ!

入力の例として「どう書く?org」の前文をお借りしました。ご自分でヤホーで調べたりして手ごろな文章を見つけて下しあ。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<入力例>
ドウカク org ヘ ヨウコソ
コノ サイト ハ ダサレタ オダイ ヲ イカニ トクカ キソイアウ
 プログラマ ノ タメノ コロシアム デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガメテ ミタイ カタ ハ ゲンゴ ノ イチラン ガ オススメ デス

<出力例>
ドウモク org ヘ ヨウコソ
コノ ザレゴト ハ ダサレタ オダイ ヲ イワハダ トシシタ キソイアウ
 プログラマ ノ タチノミ コロシアム デス
トウコウ ヲ ハナシテ ミチノリ カタ ハ プリント
トリアエズ ナガメテ ミツリン カタ ハ ゲンゴ ノ キンラン ガ オススメ デス

Posted feedbacks - Ruby

 JavaとSmalltalkの投稿を参考に書いてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#! /usr/bin/ruby -Ks

class LevenshteinDistance
    def self.distance(a, b)
        table = Array.new(a.split('').size + 1) do |i|
            i == 0 ? (0..b.split('').size).to_a : Array.new(b.split('').size).unshift(i)
        end
        (1..table.size - 1).each do |i|
            (1..table[i].size - 1).each do |j|
                table[i][j] = [
                        table[i - 1][j] + 1,
                        table[i][j - 1],
                        table[i - 1][j - 1] + (a.split('')[i - 1] == b.split('')[j - 1] ? 0 : 1)
                    ].min
            end
        end
        table.last.last
    end
end

class Knights
    attr_accessor :dictionary
    def initialize(dict_file)
        self.dictionary = []
        File.open(dict_file) { |f| f.each { |line| self.dictionary.concat(line.split(/\s+/)) } }
        self
    end
    def translate(source)
        source.split(/\s+/).map do |w|
            list = self.dictionary.select { |d| yield(w, d) }.unshift(w)
            list[rand(list.size)]
        end
    end
end

source = <<EOS
ドウカク org ヘ ヨウコソ
コノ サイト ハ ダサレタ オダイ ヲ イカニ トクカ キソイアウ
プログラマ ノ タメノ コロシアム デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガメテ ミタイ カタ ハ ゲンゴ ノ イチラン ガ オススメ デス
EOS
knights = Knights.new("./path/to/word_list.txt")
puts "入力:\n#{source}"
puts "出力:"
source.each_line do |line|
    puts(
        knights.translate(line) do |w, d|
            LevenshteinDistance.distance(w, d).to_f / [w.split('').size, d.split('').size].sort.first < 0.4
        end.join(" ")
    )
end
puts "出力:"
source.each_line do |line|
    puts(
        knights.translate(line) do |w, d|
            (w.split('').size - d.split('').size).abs <= 1 && w.split('').first == d.split('').first && w.split('').last == d.split('').last
        end.join(" ")
    )
end

 レーベンシュタイン距離の計算処理に間違いがあったので修正したものを。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#! /usr/bin/ruby -Ks

class LevenshteinDistance
    def self.distance(a, b)
        table = Array.new(a.split('').size + 1) do |i|
            i == 0 ? (0..b.split('').size).to_a : Array.new(b.split('').size).unshift(i)
        end
        (1..table.size - 1).each do |i|
            (1..table[i].size - 1).each do |j|
                table[i][j] = [
                        table[i - 1][j] + 1,
                        table[i][j - 1] + 1,
                        table[i - 1][j - 1] + (a.split('')[i - 1] == b.split('')[j - 1] ? 0 : 1)
                    ].min
            end
        end
        table.last.last
    end
end

class Knights
    attr_accessor :dictionary
    def initialize(dict_file)
        self.dictionary = []
        File.open(dict_file) { |f| f.each { |line| self.dictionary.concat(line.split(/\s+/)) } }
        self
    end
    def translate(source)
        source.split(/\s+/).map do |w|
            (list = self.dictionary.select { |d| yield(w, d) }).empty? ? w : list[rand(list.size)]
        end
    end
end

source = <<EOS
ドウカク org ヘ ヨウコソ
コノ サイト ハ ダサレタ オダイ ヲ イカニ トクカ キソイアウ
プログラマ ノ タメノ コロシアム デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガメテ ミタイ カタ ハ ゲンゴ ノ イチラン ガ オススメ デス
EOS
puts "入力:\n#{source}"
puts "出力:"
knights = Knights.new("/path/to/wordlist.txt")
source.each_line do |line|
    puts(
        knights.translate(line) do |w, d|
            LevenshteinDistance.distance(w, d).to_f / [w.split('').size, d.split('').size].max < 0.5
        end.join(" ")
    )
end
puts "出力:"
source.each_line do |line|
    puts(
        knights.translate(line) do |w, d|
            (w.split('').size - d.split('').size).abs <= 1 && w.split('').first == d.split('').first && w.split('').last == d.split('').last
        end.join(" ")
    )
end

なるべく面白くボケる様に調整してみましたが、その所為でマジックナンバー溢れるヘンなコードになってしまいました。

あと一々単語データ全部について編集距離を計算してるので、Ruby1.8.x系だと遅くてお話になりません。なんともはや……。

--

ドウヤク org ヘ ヨワミソ
コノ サイト ハ ダサレタ オダイ ヲ イチニン トクカ キソイアウ
プログラマ ノ タメノ コロシヤ デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガソデ ミタマヤ カタ ハ ゲンタツ ノ イモバン ガ オツトメ デス
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
if RUBY_VERSION >= "1.9"
  class Array
    def choice
      self[ rand(self.size) ]
    end
  end
end

def levenstein(a, b)
  ld = Array.new(a.size+1){ Array.new(b.size+1){0} }
  0.upto(a.size){|i|
    ld[i][0] = i
  }
  0.upto(b.size){|i|
    ld[0][i] = i
  }
  1.upto(a.size){|i|
    1.upto(b.size){|j|
      t = a[i-1] == b[i-1] ? 0 : 1
      ld[i][j] = [ld[i-1][j]+1, ld[i][j-1]+1, ld[i-1][j-1] + t].min
    }
  }
  return ld[-1][-1]
end

DIC = File.read("words.dat").encode("UTF-8").split(/\s+/m)

def knights(text)
  text.each_line.map{|ln|
    ln.split(" ").map{|wd|
      DIC.find_all{|w2| levenstein(wd, w2)*15/(wd.size+w2.size) < rand(6) && wd[0] == w2[0]}.choice || wd
    }.join(" ")
  }.join($/)
end

if $0 == __FILE__
  puts knights($<.read)
end

Index

Feed

Other

Link

Pathtraq

loading...