それでも @mametter ならっ・・・! RT @bonotake: quineの作りがいがありそうな…作んないけど RT @masahiro_sakai: ジョジョ言語わろた http://bit.ly/d0RUUR
http://twitter.com/masahiro_sakai/status/9091442089
brainfuck の命令文字を置換しただけの俺言語は結構ある *1 ので、いちいち quine を作るのは面白くありません。そこで、命令文字の割り当てから、その言語での quine を自動生成するソリューションを開発しました。
brainfuck の quine の生成。
$ ruby19 bfd-quine-gen.rb bf > quine.bf $ beef quine.bf > quine2.bf $ diff quine.bf quine2.bf
たぶん一番有名な置換言語 Ook! での quine 。生成物。
$ ruby19 bfd-quine-gen.rb ook > quine.ook $ java Ook quine.ook quine.bf $ beef quine.bf > quine2.ook $ diff quine.ook quine2.ook
ジョジョ言語 バージョン3 での quine *2 。生成物。
$ ruby19 bfd-quine-gen.rb jojo > quine.jojo $ ruby jojo.rb quine.jojo > quine2.jojo $ diff quine.jojo quine2.jojo
1.8 だと実行に 3 分くらいかかりました。1.9 だと 1 分くらい *3 。1.9 は素晴らしい。
以下ソース。
# coding: UTF-8 case ARGV.first when "bf" INSN_TABLE = { "<" => "<", ">" => ">", "+" => "+", "-" => "-", "[" => "[", "]" => "]", "." => ".", } when "ook" INSN_TABLE = { "<" => "Ook? Ook. ", ">" => "Ook. Ook? ", "+" => "Ook. Ook. ", "-" => "Ook! Ook! ", "[" => "Ook! Ook? ", "]" => "Ook? Ook! ", "." => "Ook! Ook. ", } when "jojo" $JOJOBUG = "+-" INSN_TABLE = { "<" => "ロードローラ", ">" => "スターフィンガ", "+" => "オラ", "-" => "無駄", "[" => "あ・・・ありのまま今起こったことを話すぜ", "]" => "ザ・ワールド", "." => "ハーミットパープル", } end def adjust(p1, p2, c1, c2) p1 < p2 ? c1 * (p2 - p1) : c2 * (p1 - p2) end def gen_code(assign) code = <<-END.gsub(/#.*|\s/, "") # load DATA (run length compressed) #>>>(len)>(char)>(len)>(char)>... # outputs code that outputs DATA >#$JOJOBUG<[<]out(1,#$JOJOBUG>>#$JOJOBUG)>[<out(1,>)>[-<out(1,+)<+>>]>]<<< # outputs code decoded from DATA [<]>[> -[-[-[-[-[-[- <[-<out(2,#{ assign[6] })>]> ]<[-<out(2,#{ assign[5] })>]> ]<[-<out(2,#{ assign[4] })>]> ]<[-<out(2,#{ assign[3] })>]> ]<[-<out(2,#{ assign[2] })>]> ]<[-<out(2,#{ assign[1] })>]> ]<[-<out(2,#{ assign[0] })>]> >]++++++++++. END nil while code.gsub!("><", "") code.gsub!(/out\((\d+),([<>+\-\[\]\.]+)\)/) do pos, buffs, str = 0, [0] * $1.to_i, "" $2.each_char do |insn| INSN_TABLE[insn].each_byte do |byte| diffs = buffs.map.with_index do |buff, idx| [idx, adjust(pos, idx, "<", ">") + adjust(buff, byte, "+", "-")] end pos, s = diffs.min_by {|idx, s| s.size } buffs[pos] = byte str << s << "." end end buffs.each_with_index.reverse_each do |buff, idx| next if buff == 0 str << adjust(pos, idx, "<", ">") << "[-]" pos = idx end str << adjust(pos, 0, "<", ">") end end code = gen_code(%w(+ - . > [ < ])) h = Hash.new(0) code.scan(/(.)\1*/) {|insn| h[insn] += 1 } assign = h.sort_by {|k, v| -v }.map {|k, v| k.first } code = gen_code(assign) data = code.gsub(/(.)\1*/) { ">#{ "+" * $&.size }>+#{ "+" * assign.index($1) }" } code = "#$JOJOBUG>>#$JOJOBUG" + data + code puts code.gsub(/./) { INSN_TABLE[$&] }