Ruby on Morse

Rubyのソースコード暗号化する「RubyEncoder」発売へ − @IT を見て、よーしパパ今から便乗して一儲けしちゃうぞー、と思ったとか思わなかったとか。

Rubyソースコードモールス符号化する「Ruby on Morse」発表へ

Rubyソースコードモールス符号化するソフトウェア、「Ruby on Morse」を国内発表するかもしれないと発表した。

Ruby on Morseの特徴
デモンストレーション

ソース (demo.rb)

require 'mruby'
encode = MRuby.encode($*.shift)
p encode
MRuby.run(encode)

実行

% ruby demo.rb 'puts "Hello world"'
"---- ---. -. -.- . --... -..-... - -.-- -.-- -..- . ----- -..- --- -.-- -.-. --..."
Hello world
Ruby on Morse ソースコード
class MRuby
	def MRuby.run(input)
		eval(decode(input))
	end

	def MRuby.decode(input)
		decoded = ''
		input.split(/\s+/).each do |c|
			raise "undefined code \"#{c}\"" unless @@morse_code[c]
			decoded += @@morse_code[c]
		end
		decoded
	end

	def MRuby.encode(input)
		encoded = [] 
		rmorse_code = @@morse_code.invert
		input.split(//).each do |c|
			raise "undefined character \"#{c}\"" unless rmorse_code[c]
			encoded << rmorse_code[c]
		end
		encoded.join(' ')
	end

	@@morse_code = {
		"."=>" ",
		"-"=>"e",
		"-."=>"t",
                  :
                省略 (下記のモールス符号表を参照)
                  :
		"--....-"=>"\e"
	}
end
Ruby on Morse モールス符号
  1. Ruby 1.9.1のライブラリのソースから各文字の出現回数を求めた。
  2. 出現回数の多い文字から 0, 1, 2 ... と小さい数を割り当てた。
  3. 割り当てた数の立っているビットに "-" を、立っていないビットに "." を割り当てた。
  4. 先頭の連続する "." は削除する(最頻出文字の場合は最後の "." を残す)。

短点「.」、長点「-」、文字間隔:空白

. e - t -. n -- \n -.. s -.-
a --. r --- i -... o -..- d -.-. l -.--
c --.. m --.- u ---. p ---- f -.... h -...-
_ -..-. . -..-- # -.-.. = -.-.- g -.--. , -.---
" --... ) --..- ( --.-. : --.-- b ---.. y ---.-
v ----. w ----- ' -..... T -....- k -...-. E -...--
x -..-.. - -..-.- @ -..--. R -..--- S -.-... [ -.-..-
] -.-.-. 0 -.-.-- -.--.. I -.--.- A -.---. / -.----
| --.... C --...- P --..-. > --..-- { --.-.. } --.-.-
1 --.--. + --.--- O ---... N ---..- ? ---.-. L ---.--
\ ----.. D ----.- M -----. F ------ \t -...... 2 -.....-
q -....-. G -....-- * -...-.. U -...-.- j -...--. z -...---
H -..-... B -..-..- % -..-.-. $ -..-.-- 3 -..--.. & -..--.-
Y -..---. X -..---- ! -.-.... 4 -.-...- 6 -.-..-. ; -.-..--
5 -.-.-.. 9 -.-.-.- 8 -.-.--. V -.-.--- W -.--... 7 -.--..-
K -.--.-. ^ -.--.-- ~ -.---.. J -.---.- Z -.----. Q -.-----
` --..... \e --....-
符号表の作り方

コマンド

% find /usr/lib/ruby/1.9.1/ -name "*.rb"  | xargs cat > rubylibs.rb
% ruby mk_morse.rb rubylibs.rb

スクリプト (mk_morse.rb)

# -*- coding: utf-8 -*-
require 'pp'
# 文字の出現回数を数える
rank = {}
while line = gets
  begin
    line.split(//).each do |c|
        next if c.ord > 0x7f
        rank[c]  = 0 unless rank[c]
        rank[c] += 1
    end
  rescue
    # split が失敗する行が数行ある
  end
end

# 符号表をつくる
i = 0
morse = {}
while rank.size > 0
  char = rank.max do |a, b|
    # 出現回数が同じだったらキーの辞書順
    a[1] == b[1] ? a[0] <=> b[0] : a[1] <=> b[1]
  end
  code = i.chr.unpack('B*').shift
  code.gsub!(/^0*([01]+)$/, '\\1') # 先頭の 0 を削除
  code.gsub!(/1/, '-')
  code.gsub!(/0/, '.')
  morse[char[0]] = code
  i += 1
  rank.delete(char[0])
end
pp morse
反省

ハフマン符号の符号化コードを作っていたときに、これってモールス信号になるんじゃない?と思ったのが切っ掛けだった。
でも、作った結果をみて愕然とした。ハフマン符号の場合は最頻出文字が "0"になり、その次が "10"、その次が "110"。そう、先頭に '1' が追加されて行くだけ。これでモールス信号はできなくはないけど、符号は "-." "--." "----." ... となり面白くないということに気付いてしまった。
ハフマン符号の符号がそのようになるのは事前にわかっていたのに、勢いで進んでしまった。最終的にでき上がったコードはハフマン符号とは全く関係ないものになってしまった。何やってるんだか。