[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[QUIZ] Morse Code (#121

James Gray

4/20/2007 12:16:00 PM

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rub...

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

The idea for this quiz was given to me by Yossef Mendelssohn.

Morse code is a way to encode telegraphic messages in a series of long and short
sounds or visual signals. During transmission, pauses are used to group letters
and words, but in written form the code can be ambiguous.

For example, using the typical dot (.) and dash (-) for a written representation
of the code, the word ...---..-....- in Morse code could be an encoding of the
names Sofia or Eugenia depending on where you break up the letters:

...|---|..-.|..|.- Sofia
.|..-|--.|.|-.|..|.- Eugenia

This week's quiz is to write program that displays all possible translations for
ambiguous words provided in code.

Your program will be passed a word of Morse code on STDIN. Your program should
print all possible translations of the code to STDOUT, one translation per line.
Your code should print gibberish translations in case they have some meaning for
the reader, but indicating which translations are in the dictionary could be a
nice added feature.

We will only focus on the alphabet for this quiz to keep things simple. Here
are the encodings for each letter:

A .- N -.
B -... O ---
C -.-. P .--.
D -.. Q --.-
E . R .-.
F ..-. S ...
G --. T -
H .... U ..-
I .. V ...-
J .--- W .--
K -.- X -..-
L .-.. Y -.--
M -- Z --..

27 Answers

Harry

4/22/2007 12:22:00 PM

0

On 4/20/07, Ruby Quiz <james@grayproductions.net> wrote:
>
> This week's quiz is to write program that displays all possible translations for
> ambiguous words provided in code.
>
> Your program will be passed a word of Morse code on STDIN. Your program should
> print all possible translations of the code to STDOUT, one translation per line.
>

This is my first Ruby Quiz.
I am interested in seeing how it can be made to run faster and also
how other people approached it.
#####

letters = Hash.new("*")
abc = %w[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]
marks = [".-","-...","-.-.","-..",".","..-.","--.","....",
"..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.",
"...","-","..-","...-",".--","-..-","-.--","--.."]

marks.each do |x|
letters.store(x,abc[marks.index(x)])
end

puts "Enter Morse code"
str = gets.chomp
str_arr = str.split(//)

nums = []
(0..5 ** str.length/4).each do |b|
if b.to_s(5) !~ /0/
sum = 0
b.to_s(5).split(//).each {|hj| sum += hj.to_i }
if sum == str.length
nums << b.to_s(5)
end
end
end

unpackers = []
nums.each do |x|
unpackers << x.to_s.split(//).collect {|u| "A" + u}.join
end

morse = []
unpackers.each do |g|
morse << str.unpack(g)
end

words = []
morse.each do |t|
word = ""
t.each do |e|
word << letters[e]
end
words << word unless word =~ /\*/
end
puts words

###

Harry

--
http://www.kakueki.com/ruby...
A Look into Japanese Ruby List in English

Christian Theil Have

4/22/2007 12:42:00 PM

0

Hi

I've implemented it using a simple backtracking search algorithm.

My code could probably be a lot more compact, and the first_letters
function could definitely be
much faster..


class Morse
@@alpha = {
"a" => ".-",
"b" => "-...",
"c" => "-.-.",
"d" => "-..",
"e" => ".",
"f" => "..-.",
"g" => "--.",
"h" => "....",
"i" => "..",
"j" => ".---",
"k" => "-.-",
"l" => ".-..",
"m" => "--",
"o" => "---",
"p" => ".--.",
"q" => "--.-",
"r" => ".-.",
"s" => "...",
"t" => "-",
"u" => "..-",
"v" => "...-",
"w" => ".--",
"x" => "-..-",
"y" => "-.--",
"z" => "--.."
}

def initialize
# Reverse index the array
@rev = {}
@@alpha.each { |k,v| @rev[v] = k.to_s }
end

# Returns all letters matching the morse str at this pos
def first_letters(morse, pos)
letters = []
@rev.keys.each { |k| letters << k unless
morse[pos..-1].scan(/^#{k.gsub(".","\\.")}.*/).empty? }

letters
end

# Returns an array of words that matches 'morse' string
# It's basically a recursive function with bactracking
def morse2words(morse, pos = 0 , seen = "")
solutions = []
first_letters(morse, pos).each do |l|
if morse.length == pos + l.length
solutions << "#{seen}#{@rev[l]}"
else
result = morse2words(morse,(pos+l.length),"#{seen}#{@rev[l]}")
solutions += result
end
end

solutions
end

# Converts a word to a morse string, used for testing
def word2morse(word)
morse = ""
word.each_byte { |b| morse << @@alpha[b.chr] }

morse
end
end


######################
# Test:

def test_word2morse
m = Morse.new
raise unless m.word2morse("sofia") == "...---..-....-"
end

def test_first_letters
m = Morse.new
raise unless m.first_letters(".", 0) == [ "." ];
raise unless m.first_letters("--.--..--.-.", 0) == ["--", "-", "--.", "--.-"]
end

def test_morse2words
m = Morse.new
sofia = "...---..-....-"
solutions = m.morse2words(sofia)
solutions.each do |s|
if m.word2morse(s) != sofia
puts "bad solution: #{s}"
puts "yields #{m.word2morse(s)} in morse"
raise
end
end
end

test_word2morse
test_first_letters
test_morse2words

Carl Porth

4/22/2007 12:57:00 PM

0

Here is mine using simple recursion.

#!/usr/bin/env ruby -wKU

require "set"

WORD_FILE = "/usr/share/dict/words"
MORSE_LETTERS = %w[.- -... -.-. -.. . ..-. --. .... .. .--- -.- .-..
--
-. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --..]

# map encodings to letters
ENCODINGS = Hash[*MORSE_LETTERS.zip(('A'..'Z').to_a).flatten]

def morse_decodings(word)
# iterate through matching prefixes
ENCODINGS.select { |p,l| p == word[0,p.size] }.map do |
prefix,letter|

# gather decoded suffixes for the current prefix
suffixes = morse_decodings( word[prefix.size,word.size] )

# append decoded suffixes to decoded letter
suffixes.empty? ? letter : suffixes.map { |s| letter + s }

end.flatten
end

decodings = morse_decodings(readline.chomp).sort

puts "All Possible Decodings:"
decodings.each { |e| puts e }

# read word file into set (for fast indexing)
words = Set.new
open(WORD_FILE).each { |line| words << line.chomp.upcase }

puts "All Decodings in Dictionary:"
decodings.each { |e| puts e if words.include? e }

__END__

Carl Porth


Jesse Merriman

4/22/2007 1:11:00 PM

0

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.jessemer...

Drew Olson

4/22/2007 1:35:00 PM

0

This is my first rubyquiz as well. My solution uses simple recursion and
also checks matches against a dictionary. For fun, I also checked the
frequency of words within a holmes novel to determine probabilistically
which match was the most likely (we could train on any number of text
documents, just chose this one for fun).

Here's my solution:

# file: morse_trained.rb
# author: Drew Olson

# the train method is based on this rubytalk post:
# http://www.ruby-...topic/...
#
# we build a model based on the frequency of words
# within the text provided here: http://www.norvig.com/...
combined
# with the frequency in the local dictionary. this means any word in the
dictionary
# will have a frequency of 1 and words appearing in the holmes text will
have
# increased frequencies, thus being favored in the sort later in the
program.
# the goal is the present the user with the most relevant matches first.
# both files were saved locally.
def train texts
model = Hash.new(0)
texts.each do |text|
File.new(text).read.downcase.scan(/[a-z]+/).each do |word|
model[word] += 1
end
end
return model
end

# global hash of word -> frequency pairs
NWORDS = train ['holmes.txt','dictionaries/2of4brif.txt']

# MorseLetter holds a pattern and the letter associated
# with the pattern
MorseLetter = Struct.new(:pattern,:letter)

# global array to hold all the MorseLetter objects
LETTERS = [MorseLetter.new(/^\.-/,"A"),
MorseLetter.new(/^-\.\.\./,"B"),
MorseLetter.new(/^-\.-\./,"C"),
MorseLetter.new(/^-\.\./,"D"),
MorseLetter.new(/^\./,"E"),
MorseLetter.new(/^\.\.-\./,"F"),
MorseLetter.new(/^--\./,"G"),
MorseLetter.new(/^\.\.\.\./,"H"),
MorseLetter.new(/^\.\./,"I"),
MorseLetter.new(/^\.---/,"J"),
MorseLetter.new(/^-\.-/,"K"),
MorseLetter.new(/^\.-\.\./,"L"),
MorseLetter.new(/^--/,"M"),
MorseLetter.new(/^-\./,"N"),
MorseLetter.new(/^---/,"O"),
MorseLetter.new(/^\.--\./,"P"),
MorseLetter.new(/^--\.-/,"Q"),
MorseLetter.new(/^\.-\./,"R"),
MorseLetter.new(/^\.\.\./,"S"),
MorseLetter.new(/^-/,"T"),
MorseLetter.new(/^\.\.-/,"U"),
MorseLetter.new(/^\.\.\.-/,"V"),
MorseLetter.new(/^\.--/,"W"),
MorseLetter.new(/^-\.\.-/,"X"),
MorseLetter.new(/^-\.--/,"Y"),
MorseLetter.new(/^--\.\./,"Z")]

# a recursive method which checks the code for letter matches,
# builds the translation string, removes the matched
# portion of the code and then recurses
#
# the method returns an array of all possible morse code translations
def translate code, translation = ""

# recursion base case:
#
# return an array containing the translation if the code has
# a size of 0
return [translation.downcase] if code.size.zero?

words = []

# check all possible matches to the code
LETTERS.each do |letter|
if code[letter.pattern]

# recurse on untranslated portion of the code
# and new translation
# add results to our array at this level of recursion
words += translate
code.sub(letter.pattern,''),translation+letter.letter
end
end

return words

end

# read the initial code from standard input
code = gets.chomp

# initial call to translate with the complete code
# and no translation string
words = translate code

# sort the resulting words first based on the frequency in NWORDS
# and the dictionary in a decreasing order and then by the word itself.
this
# preserves alphabetical order when words have the same frequency or
# do not appear in the dictionary. we then print each word along with an
# asterisk if that word is in the dictionary (or in the training
material
# but not in the dictionary).
words.sort_by{|word| [-NWORDS[word],word] }.each do |word|
puts "#{word.capitalize} #{"*" if NWORDS[word] > 0}"
end

--
Posted via http://www.ruby-....

I. P.

4/22/2007 1:38:00 PM

0

|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/l...

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


Joseph Seaton

4/22/2007 1:40:00 PM

0

This is my first solution that I've posted (and my first post to
ruby-talk) so please be nice to me, I'm only a newbie.

#####
require 'rubygems'
gem 'raspell'
require 'raspell'

class MCheck
def initialize(message, mmap)
@aspell = Aspell.new
@mmap = mmap
matches(message)
end
def matches(str,s="") #recursively check string for
@mmap.each do |n,o| #every possible letter
if str =~ /^#{n}(.*)$/
num = "#{s}#{@mmap[n]}"
if $1 == ""
x = @aspell.check(num) ? "*" : " "
puts " #{x} #{num}"
else
matches($1, num)
end
end
end
end
end
MCheck.new(gets.gsub(/[^\.\-]+/, ''), Hash[*"A .- N -.
B -... O ---
C -.-. P .--.
D -.. Q --.-
E . R .-.
F ..-. S ...
G --. T -
H .... U ..-
I .. V ...-
J .--- W .--
K -.- X -..-
L .-.. Y -.--
M -- Z --..".gsub(/(\.|\-)/, '\\\\\1').split(/\n| /)].invert) #Escape .
and - for regexx, and create hash
#####

My solution uses aspell, with the raspell ruby bindings, so it might be
a pain to get working, but it's fairly simple to remove the
spell-checking code. I haven't checked how efficient this code is, I
think the efficiency of the terminal used at printing out all the
solutions probably has more effect on speed.

Joe

Kyle Schmitt

4/22/2007 1:47:00 PM

0

#morse code
#a little inefficient, but easy to follow
letters2morse = {"k"=>"-.-", "v"=>"...-", "l"=>".-..", "w"=>".--",
"a"=>".-", "m"=>"--", "x"=>"-..-",
"b"=>"-...", "y"=>"-.--", "c"=>"-.-.",
"n"=>"-.", "z"=>"--..", "d"=>"-..", "o"=>"---",
"e"=>".", "p"=>".--.", "f"=>"..-.",
"q"=>"--.-", "g"=>"--.", "r"=>".-.", "h"=>"....",
"s"=>"...", "i"=>"..", "t"=>"-",
"j"=>".---", "u"=>"..-"}
morse2letters = {}
letters2morse.each_pair do
|letter,morse|
morse2letters.store(morse,letter)
end

#for testing
#stringtoconvert = "Sofia".downcase
#encodedstring =stringtoconvert.split('').collect{|i| letters2morse[i]}.join

puts "Enter a word in morse code"
encodedstring = gets().gsub(/[^.-]/,'')#and ignore anything that's not morse

#seed the hash. the value of each key is the number of times the word was found
#just through it may be interesting later on
huge={encodedstring,0}
huge.default=0

#while anything in the hash has non-morse chars
while(huge.keys.join[/[.-]/]!=nil)
huge.keys.each do
|key|
if key[/[.-]/]!=nil
morse2letters.each_pair do
|code,letter|
huge.store(key.sub(code,letter),huge[key.sub(code,letter)]+1)
#for each letter of the alphabet, create a new value by replacing
#the first instance of the letter with it's morse value, and insert it
#into the hash.
end
#encoded all possibilities, now delete it.
huge.delete(key)
else
#continuous output when answers are found
#puts key
end
end
end
puts huge.keys.sort

Kyle Schmitt

4/22/2007 1:52:00 PM

0

OOps, that comment is poorly worded.


On 4/22/07, Kyle Schmitt <kyleaschmitt@gmail.com> wrote:
> #morse code
.....................
> #for each letter of the alphabet, create a new value by replacing
> #the first instance of the letter with it's morse value, and insert it
> #into the hash.

For each letter of the morse alphabet, insert a new key by replacing
the first match of that letter-code with the letter.

Erik Veenstra

4/22/2007 4:05:00 PM

0

* O(c^n)?... :{ (It's like a non-deterministic finite state
machine with only one state...)

* Includes the dictionary words.

* No state.

How it works? find_words() takes a (partial) word and a
sequence, starting with an empty string and the input sequence.
If "._" could be "shifted" from the sequence, the character "a"
is added to the (partial) word and find_words() is called with
the new (partial) word and the remaining sequence as sequence.
This is done for all characters of the alphabet (iteration) and
all "characters" in the sequence (recursion), until
find_words() receives an empty sequence, in which case the word
is a final word.

["", ".--....-..."]
["a", "-....-..."]
["ab", ".-..."]
["aba", "..."]
["abae", ".."]
["abaee", "."]
["abaeee", ""] <-- Final word.
["abaei", ""] <-- Final word.
["abai", "."]
["abaie", ""] <-- Final word.

gegroet,
Erik V. - http://www.erikve...

----------------------------------------------------------------

$ cat morse.rb
class Morse
MORSE_CODES = %w{.- -... -.-. -.. . ..-. --. .... .. .---
-.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.--
--..}.zip(("a".."z").to_a)

DICTIONARY_WORDS = File.open("/usr/share/dict/words"){|f|
f.read}.downcase.split(/[^a-z]/) rescue nil

def parse(sequence)
real_words(find_words(sequence.gsub(/\s+/, "")))
end

private

def find_words(sequence, word="", results=[])
if sequence.empty?
results << word
else
MORSE_CODES.each do |seq, chr|
if sequence.index(seq) == 0
find_words(sequence[seq.length..-1], word+chr, results)
end
end
end

results
end

def real_words(words)
words & DICTIONARY_WORDS rescue words
end
end

puts Morse.new.parse($stdin.read)

----------------------------------------------------------------

$ echo ".--....-..." | ruby morse.rb
abets
able
adele
pile
wests

----------------------------------------------------------------