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 モールス符号表
- Ruby 1.9.1のライブラリのソースから各文字の出現回数を求めた。
- 出現回数の多い文字から 0, 1, 2 ... と小さい数を割り当てた。
- 割り当てた数の立っているビットに "-" を、立っていないビットに "." を割り当てた。
- 先頭の連続する "." は削除する(最頻出文字の場合は最後の "." を残す)。
短点「.」、長点「-」、文字間隔:空白
. | 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' が追加されて行くだけ。これでモールス信号はできなくはないけど、符号は "-." "--." "----." ... となり面白くないということに気付いてしまった。
ハフマン符号の符号がそのようになるのは事前にわかっていたのに、勢いで進んでしまった。最終的にでき上がったコードはハフマン符号とは全く関係ないものになってしまった。何やってるんだか。