From: Jesse Merriman Date: 2007-04-22T22:10:56+09:00 Subject: [QUIZ][SOLUTION] Morse Code (#121) My solution is short enough that I don't need to explain the code outside of my comments. For input, the first line is the morse code to translate, and every line after (until EOF) is one dictionary word (case insensitive). It can be called with or without a --matching command-line option. Without it, all translations are printed, and those in the dictionary are marked. With it, only translations matching a dictionary word are printed. On my Linux machine, I was able to dump aspell's dictionary to do things like this: jesse@ricercar $ echo ...---..-....- > input jesse@ricercar $ aspell dump master en_US >> input jesse@ricercar $ ./morse_code.rb --matching < input Enter morse code to translate: Enter dictionary words (case does not matter, EOF when done): Translations: EUGENIA SOUSA SOFIA The code: #!/usr/bin/env ruby # morse_code.rb # Ruby Quiz 121: Morse Code require 'set' Decode = { '.-' => 'A', '-...' => 'B', '-.-.' => 'C', '-..' => 'D', '.' => 'E', '..-.' => 'F', '--.' => 'G', '....' => 'H', '..' => 'I', '.---' => 'J', '-.-' => 'K', '.-..' => 'L', '--' => 'M', '-.' => 'N', '---' => 'O', '.--.' => 'P', '--.-' => 'Q', '.-.' => 'R', '...' => 'S', '-' => 'T', '..-' => 'U', '...-' => 'V', '.--' => 'W', '-..-' => 'X', '-.--' => 'Y', '--..' => 'Z' } # Could hard-code these, but what fun would that be? MinCodeLength = Decode.keys.min { |k,j| k.length <=> j.length }.length MaxCodeLength = Decode.keys.max { |k,j| k.length <=> j.length }.length class Array # Yield once for each way of grouping the elements into groups of size # between min_length and max_length (inclusive). It works recursively: # empty arrays return self, and longer arrays take all initial (head) # slices of valid lengths, and append to that each grouping of the # remaining tail. def each_grouping(min_length, max_length) if empty? yield self else max_length = size if size < max_length (min_length..max_length).each do |code_length| head = [slice(0, code_length)] slice(code_length..-1).each_grouping(min_length, max_length) do |tail| yield head + tail end end end end end class String # Yield once for each translation of this (Morse code) string. def each_translation split(//).each_grouping(MinCodeLength, MaxCodeLength) do |group| # Convert arrays of individual dots & dashes to strings, then translate. group.map! { |char_arr| Decode[char_arr.join] } # Skip if something was not translated. next if group.any? { |letter| letter.nil? } # Join all the translated letters into one string. yield group.join end end end if $0 == __FILE__ src = $stdin dict = Set[] if ARGV.include?('--matching') trans_handler = lambda do |trans| puts trans if dict.include? trans end else trans_handler = lambda do |trans| print trans dict.include?(trans) ? puts(' <-- In dictionary!') : puts end end puts 'Enter morse code to translate:' code = src.gets.chomp puts 'Enter dictionary words (case does not matter, EOF when done):' while dict_word = src.gets dict << dict_word.chomp.upcase end puts 'Translations:' code.each_translation { |trans| trans_handler[trans] } end -- Jesse Merriman jessemerriman@warpmail.net http://www.jessemerriman.com/