|Ruby Quiz| Straightforward and memory-greedy solution. = Tables == table.rb # An interface to subsequent tables class Table def initialize @table = {} compose end def compose end def [](value) @table[value] end end == morse.rb require 'table' class Morse < Table def compose @table['.'] = 'E' @table['..'] = 'I' @table['.-'] = 'A' @table['...'] = 'S' @table['..-'] = 'U' @table['....'] = 'H' @table['...-'] = 'V' @table['..-.'] = 'F' @table['.-.'] = 'R' @table['.--'] = 'W' @table['.-..'] = 'R' @table['.--.'] = 'P' @table['.---'] = 'G' @table['-'] = 'T' @table['-.'] = 'N' @table['--'] = 'M' @table['-..'] = 'D' @table['-.-'] = 'K' @table['-...'] = 'B' @table['-..-'] = 'X' @table['-.-.'] = 'C' @table['-.--'] = 'Y' @table['--.'] = 'G' @table['---'] = 'O' @table['--..'] = 'Z' @table['--.-'] = 'Q' end end == probability.rb # I've decided to use letters probabilities approach. Output strings # will be sorted by difference (Euclidean metric) between input distribution and control # distribution for English language (taken from [1]). # However possible benefits are visual only in large texts. But large # texts are processed very lo-o-ong... # In few signals' string it's just sends less meaningful values like # 'EEEETTTEEE' to end. # In SOFIA/EUGENIA example: SOFIA was found at 1824's position and # EUGENIA at 935's from 5104 strings. # [1] http://www.fortunecity.com/skyscraper/coding/379/lesson1.htm require 'table' class Probability < Table def compose @table['E'] = 0.127 @table['T'] = 0.091 @table['A'] = 0.082 @table['O'] = 0.075 @table['I'] = 0.070 @table['S'] = 0.063 @table['N'] = 0.067 @table['H'] = 0.061 @table['R'] = 0.060 @table['L'] = 0.040 @table['C'] = 0.028 @table['U'] = 0.028 @table['M'] = 0.024 @table['W'] = 0.023 @table['F'] = 0.022 @table['G'] = 0.020 @table['P'] = 0.019 @table['B'] = 0.015 @table['V'] = 0.010 @table['K'] = 0.008 @table['J'] = 0.002 @table['Y'] = 0.002 @table['Q'] = 0.001 @table['X'] = 0.001 @table['Z'] = 0.001 end def metric(vec1, vec2) vec = [] vec1.each_index do |index| vec << [vec1[index], vec2[index]] end metr = vec.inject(0) do |sum, item| sum + (item[0]-item[1]) ** 2 end Math.sqrt(metr) end def to_vector table = @table.sort.to_a table.inject([]) do |acc, item| acc << item[1] end end def distance(other) metric(self.to_vector, other) end end = Implementation Approach: (0 step) Input Morse string is sent to accumulator (n step) Take first element from accumulator. Separate it in two parts: head with decoded letters and tail with Morse code. If tail is not empty: Find letter representation for first four codes in tail: - decode letter if it's represented by one signal - decode letter if it's represented by two signals (if possible) - decode letter if it's represented by three signals (if possible) - decode letter if it's represented by four signals (if possible) Construct new strings (no more than 4): - previously decoded head plus decoded on this step letter plus intact Morse code and append them to accumulator If tail is empty: Append to output. == telegraphist.rb require 'probability' require 'morse' require 'enumerator' class Telegraphist ALPHABET = 'ABCDEFGHIJKLMNOPQRTSUVWXYZ' # SILENT mode writes output to file SILENT = true def initialize @probability = Probability.new @morse = Morse.new end def listen @code = $stdin.gets.chomp end def say(words) if SILENT File.open("check.txt", "w") do |file| file.puts words end else $stdout.puts words end end def decode sort(translate(@code)) end def translate(text) txt = text.split(//) accumulator = [] << txt result = [] while !accumulator.empty? element = accumulator.shift if element.include?('.') or element.include?('-') head = element.select do |item| item =~ /\w/ end tail = element.select do |item| item =~ /[.-]/ end if tail.length < 4 iterate = tail.length else iterate = 4 end (1..iterate).each do |index| letter = @morse[tail[0, index].join] accumulator << head + [letter] + tail[index, element.length] if letter end else result << element.join end end result end def sort(lines) lines.sort_by do |line| @probability.distance(probabilities(line)) end end def probabilities(str) abc = ALPHABET.split(//) acc = [] abc.each_index do |index| acc[index] = str.count(abc[index]) end acc.map do |item| item / str.length.to_f end end end = Application == app.rb require 'telegraphist' operator = Telegraphist.new operator.listen operator.say(operator.decode) -- I. P. 2007-04-22T17:09