$LETTER = Hash[*%w/
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 --..
/]
$CODE = $LETTER.invert
def encode(message)
encoded_message = []
words = message.split(" ")
words.each do |word|
encoded_word = ""
word.split(//).each do |letter|
if $LETTER.has_key?(letter.upcase)
encoded_word << $LETTER[letter.upcase]
encoded_word << " "
end
end
encoded_message << encoded_word
end
puts encoded_message.join("| ")
end
def decode(message)
decoded_message = ""
words = message.split("|")
words.each do |word|
word.split(/\s/).each do |letter|
if $CODE.has_key?(letter)
decoded_message << $CODE[letter]
end
end
decoded_message << " "
end
puts decoded_message
end
def encode_file(f)
File.open(f).each do |line|
encode(line)
end
end
def decode_file(f)
File.open(f).each do |line|
decode(line)
end
end
Refactorings
No refactoring yet !
Adam
September 29, 2008, September 29, 2008 18:51, permalink
module Morse
CODES = Hash[*%w/
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 --..
\ |
/]
def self.file_helper(method)
module_eval %{
def self.#{method}_file(filename)
File.readlines(filename).map { |line| #{method}(line) }.join("\n")
end
}
end
def self.encode(message)
message.upcase.split(//).map { |char| CODES[char] }.join(' ')
end
file_helper :encode
def self.decode(message)
message.split(' ').map { |code| CODES.invert[code] }.join
end
file_helper :decode
end
Druwerd
September 30, 2008, September 30, 2008 00:36, permalink
Thanks Adam.
This is very helpful. Looks concise and elegant.
Magnus Holm
October 1, 2008, October 01, 2008 12:49, permalink
module Morse;A=[19,189,192,63,6,165,66,162,18,175,64,171,22,21,67,174,199,57,
54,7,55,163,58,190,193,198];B=proc{|c|A[c-65].to_s(3)[1..-1].tr('10','-.')}
C=proc{|m|(A.index(("2"+m.tr('-.','10')).to_i(3))||return)+65}
def self.encode(str);str.upcase.split(//).map{|c|(65..
90)===(c=c[0])?B[c]:c==32?'|':nil}.compact.join(' ')
end
def self.decode(str);str.split(' ').map{|m|m[0]==?|?' ':(m=C[m])&&m.chr}.join
end
self.methods(false).map{|m|class<<self;self;end.send(:define_method,m+"_file"
){|f|File.readlines(f).map{|l|send(m,l)}.join $/}}
end
# >> Morse.encode("Concise and elegant")
# => "-.-. --- -. -.-. .. ... . | .- -. -.. | . .-.. . --. .- -. -"
# >> Morse.decode("-.-. --- -. -.-. .. ... . | .- -. -.. | . .-.. . --. .- -. -")
# => "CONCISE AND ELEGANT"
# >> Morse.encode_file("somefile.txt")
# >> Morse.decode_file("somefile.txt")
Druwerd
October 1, 2008, October 01, 2008 18:45, permalink
Magnus,
I don't understand your approach at all, but it sure looks concise!
Could you explain how it works?
lel
October 18, 2008, October 18, 2008 15:25, permalink
Juggling ascii-numbers, base 3 and single letter constant names? Is this "obfuscatemycode.com" :-)
# The idea is that each morse code is encoded as a number: # "a" is mapped to 19, which in base 3 is 201, the 2 works as a dummy to keep leading 0's in place, so the real value is "01" which maps to the morse code ".-" # "b" is mapped to 189, which in base 3 is 21000, remove the 2 and the real value is "1000" which maps to the mores code "-..." # and so on. # # I don't see why not, but the codes could have been encoded in base 2 as well: # "a" would map to 5, which in base 2 is "101", the first digit is always a dummy, so the real value is "01" which maps to ".-" # "b" would map to 24, which in base 2 is "11000", skip the first digit and we get "1000" which maps to "-..." #
Magnus Holm
October 19, 2008, October 19, 2008 18:28, permalink
lel: You're absolutely right. This can be solved in a much simpler way:
module Morse
A,B,C=proc{M.tr(' ','').gsub(/#{$/}+/,$/).split($/).map{|x|
x[/(.)(.+)(.).*\1\2\3/,2].to_i(16)}},proc{|c|A[][c-65].to_s(2
)[1..-1].tr('10','-.')},proc{|m|(A[].index(("1"+m.tr(
'-.','10')).to_i(2))||return)+65}
def self.encode(str)
str.upcase.split(//).map{|c|(65..90)===
(c=c[0])?B[c]:c==32?'|':nil}.compact.join(' ')
end
def self.decode(str)
str.split(' ').map{|m|m[0]==?|?' ':(m=C[m])&&m.chr}.join
end
self.methods(false).
map{|m|class<<self;self;end.
send(:define_method,m+"_file"){|f|
File.readlines(f).map{|l|send(m,l)}.join $/}}
M = <<-'LETS GO MORSING!'
3DC5D1BBFBE596573E0EE011500C2959ED1469891C5D9120EA8
D18A6BF9C806A8A15A77A5788575BD18A0233578435B20884FE
A1AAAAAA1AA
BCD BCD
123 123
412C127 140C127
87EB9557EB757
7103B03517D082301871039DD318B6ABEB04981
2A44EFBD92A0B694FE1ECC094F42E7F7DF37AF7
9781 765A3C96E24326A0F58 1764
2D3C 3399D0D588D492D 3A95
AB74 D14522116D1 45E3
6833 7 A807B37 A F32E
BD57 4F C61 EC 613F
07EF 733E F 7DEA 540F
E285 04F691 656291 6595
85EC 1D8343A C1D82A1 9F95
F5A3 7F8D02D25A357BEDC75 4958
86C8 61F6679FFEB2DF19CEC 8673
0706 DC3C81C810CBEBA76FC CC3C
32B7 91791ADFE02C7664F6E 7D51
1111 656B15AA24FF66461FD 1111
0BD60F270C533AE418A5A99E65950BD26D003CF
1BC023CDEBA3CE19D0CF7E0B50607C7E19DD396
11B878DCDAEAE1AE136657420A26AF87356FCD6DA11B80CDD4F
22C2201C00354CD948A67CAAA92566A7E4515CAADADC8FA01C0
LETS GO MORSING!
end
I'm trying to improve my Ruby skills. I wrote this as an exercise. I would like some suggestions on making this cleaner.