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/