Morse Code

April 28, 2009

There are dozens of ways to solve this problem. We choose an association list, which is simple and good enough. We first build a morse/character table; it is convenient to build the table twice, once in each direction:

(define morse-code '(
  (#\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  "--..") (#\1 ".----") (#\2 "..---")
  (#\3 "...--") (#\4 "....-") (#\5 ".....") (#\6 "-....")
  (#\7 "--...") (#\8 "---..") (#\9 "----.") (#\0 "-----")
  (".-"    #\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) (".----" #\1) ("..---" #\2)
  ("...--" #\3) ("....-" #\4) ("....." #\5) ("-...." #\6)
  ("--..." #\7) ("---.." #\8) ("----." #\9) ("-----" #\0)
  (#\space "")))

Converting from characters to Morse code is done by looking up each character in the table. The output is built in reverse.

(define (morse str)
  (let loop ((cs (string->list str)) (result '()))
    (if (null? cs) (list->string (cdr (reverse result)))
      (loop (cdr cs) (append (reverse (string->list
            (cadr (assoc (char-upcase (car cs)) morse-code))))
            (list #\space) result)))))

Converting back from Morse code to characters is just the opposite of the previous function:

(define (un-morse str)
  (let loop ((cs (string->list str)) (ms '()) (result '()))
    (cond ((null? cs)
            (if (null? ms)
                (list->string (reverse result))
                (list->string (reverse (cons (cadr (assoc
                    (list->string (reverse ms)) morse-code)) result)))))
          ((char=? (car cs) #\space)
            (if (null? ms)
                (loop (cdr cs) ms (cons #\space result))
                (loop (cdr cs) '() (cons (cadr (assoc (list->string
                    (reverse ms)) morse-code)) result))))
          ((or (char=? (car cs) #\.) (char=? (car cs) #\-))
            (loop (cdr cs) (cons (car cs) ms) result)))))

An example is shown below, and can be run at http://programmingpraxis.codepad.org/7Gf5N1I2.

> (un-morse (morse "Programming Praxis"))
"PROGRAMMING PRAXIS"

About these ads

Pages: 1 2

11 Responses to “Morse Code”

  1. [...] Praxis – Morse code By Remco Niemeijer Today’s Programming Praxis problem is about morse code. We’re supposed to write a program to convert [...]

  2. Remco Niemeijer said

    My Haskell solution. For a version with comments, see http://bonsaicode.wordpress.com/2009/04/28/programming-praxis-morse-code/

    import Data.Char
    import Data.List
    import Data.List.Split
    import qualified Data.Map as M
    
    plain :: [Char]
    plain = ['A'..'Z'] ++ ['0'..'9']
    
    morse :: [String]
    morse = words ".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- \
                  \-. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.. \
                  \.---- ..--- ...-- ....- ..... -.... --... ---.. ----. -----"
    
    convert :: Ord a => [a] -> [b] -> a -> b
    convert from to x = M.fromList (zip from to) M.! x
    
    toMorse :: String -> String
    toMorse = intercalate "  " . map (unwords .
                  map (convert plain morse . toUpper)) . words
    
    fromMorse :: String -> String
    fromMorse = unwords . map (map (convert morse plain) . words) . splitOn "  "
    
    main :: IO ()
    main = do print $ toMorse "Programming Praxis"
              print $ fromMorse ".--. .-. --- --. .-. .- -- -- .. -. --.  \
                                \.--. .-. .- -..- .. ..."
    
  3. Remco Niemeijer said

    Hm. Evidently this code causes the syntax highlighting plugin to be incredibly slow.

  4. Here is a factor implementation:
    I wish I could preview this comment before posting it but here goes:

    
            
                .NULL {
    color: #000000;
    }
    .COMMENT1 {
    color: #cc0000;
    }
    .COMMENT2 {
    color: #ff8400;
    }
    .COMMENT3 {
    color: #6600cc;
    }
    .COMMENT4 {
    color: #cc6600;
    }
    .DIGIT {
    color: #ff0000;
    }
    .FUNCTION {
    color: #9966ff;
    }
    .INVALID {
    background: #ffffcc;
    color: #ff0066;
    }
    .KEYWORD1 {
    color: #006699;
    font-weight: bold;
    }
    .KEYWORD2 {
    color: #009966;
    font-weight: bold;
    }
    .KEYWORD3 {
    color: #0099ff;
    font-weight: bold;
    }
    .KEYWORD4 {
    color: #66ccff;
    font-weight: bold;
    }
    .LABEL {
    color: #02b902;
    }
    .LITERAL1 {
    color: #ff00cc;
    }
    .LITERAL2 {
    color: #cc00cc;
    }
    .LITERAL3 {
    color: #9900cc;
    }
    .LITERAL4 {
    color: #6600cc;
    }
    .MARKUP {
    color: #0000ff;
    }
    .OPERATOR {
    color: #000000;
    font-weight: bold;
    }
    
                /home/toups/tinywork/praxis/morse.factor
            
            
                
    USING: ascii lists sequences regexp ;
    
    : ch>morse ch>upper ( char -- morse-str )
             { { CHAR: 0 [ "— — — — —" ] }
               { CHAR: 1 [ "• — — — —" ] }
               { CHAR: 2 [ "• • — — —" ] }
               { CHAR: 3 [ "• • • — —" ] }
               { CHAR: 4 [ "• • • • —" ] }
               { CHAR: 5 [ "• • • • •" ] }
               { CHAR: 6 [ "— • • • •" ] }
               { CHAR: 7 [ "— — • • •" ] }
               { CHAR: 8 [ "— — — • •" ] }
               { CHAR: 9 [ "— — — — •" ] }
               { CHAR: A [ "• —" ] } 
               { CHAR: B [ "— • • •" ] }
               { CHAR: C [ "— • — •" ] } 
               { CHAR: D [ "— • •" ] } 
               { CHAR: E [ "• • •" ] }
               { CHAR: F [ "• • — •" ] }
               { CHAR: G [ "— — •" ] }
               { CHAR: H [ "• • • •" ] } 
               { CHAR: I [ "• •" ] } 
               { CHAR: J [ "• — — —" ] } 
               { CHAR: K [ "— • —" ] } 
               { CHAR: L [ "• — • •" ] } 
               { CHAR: M [ "— —" ] } 
               { CHAR: N [ "— •" ] } 
               { CHAR: O [ "— — —" ] } 
               { CHAR: P [ "• — — •" ] } 
               { CHAR: Q [ "— — • —" ] } 
               { CHAR: R [ "• — •" ] } 
               { CHAR: S [ "• • •" ] } 
               { CHAR: T [ "—" ] } 
               { CHAR: U [ "• • —" ] } 
               { CHAR: V [ "• • • —" ] } 
               { CHAR: W [ "• — —" ] } 
               { CHAR: X [ "— • • —" ] }  
               { CHAR: Y [ "— • — —" ] }  
               { CHAR: Z [ "— — • •" ] } [ drop " " ] } case ;
    
    : morse>ch ( morse-substring -- char )
             { { "— — — — —" [ CHAR: 0 ] }
               { "• — — — —" [ CHAR: 1 ] }
               { "• • — — —" [ CHAR: 2 ] }
               { "• • • — —" [ CHAR: 3 ] }
               { "• • • • —" [ CHAR: 4 ] }
               { "• • • • •" [ CHAR: 5 ] }
               { "— • • • •" [ CHAR: 6 ] }
               { "— — • • •" [ CHAR: 7 ] }
               { "— — — • •" [ CHAR: 8 ] }
               { "— — — — •" [ CHAR: 9 ] }
               { "• —" [ CHAR: A ] } 
               { "— • • •" [ CHAR: B ] }
               { "— • — •" [ CHAR: C ] } 
               { "— • •" [ CHAR: D ] } 
               { "• • •" [ CHAR: E ] }
               { "• • — •" [ CHAR: F ] }
               { "— — •" [ CHAR: G ] }
               { "• • • •" [ CHAR: H ] } 
               { "• •" [ CHAR: I ] } 
               { "• — — —" [ CHAR: J ] } 
               { "— • —" [ CHAR: K ] } 
               { "• — • •" [ CHAR: L ] } 
               { "— —" [ CHAR: M ] } 
               { "— •" [ CHAR: N ] } 
               { "— — —" [ CHAR: O ] } 
               { "• — — •" [ CHAR: P ] } 
               { "— — • —" [ CHAR: Q ] } 
               { "• — •" [ CHAR: R ] } 
               { "• • •" [ CHAR: S ] } 
               { "—" [ CHAR: T ] } 
               { "• • —" [ CHAR: U ] } 
               { "• • • —" [ CHAR: V ] } 
               { "• — —" [ CHAR: W ] } 
               { "— • • —" [ CHAR: X ] }  
               { "— • — —" [ CHAR: Y ] }  
               { "— — • •" [ CHAR: Z ] }
               [ drop 32 ] } case ;
    
    
    : string>morse ( string -- morse-string )
             sequence>cons { } [ ch>morse suffix ] foldl
             "  " join ;
    
    : morse>string ( morse-string -- string )
             R/ [ ]{2,}/ re-split
             [ >string morse>ch ] map >string ; 

  5. g said

    python

    from string import lowercase, digits
    morse = dict((c, (""".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- 
                      .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.. 
                      ----- .---- ..--- ...-- ....- ..... -.... --... ---.. ----.""".split() + [" "])[i])
                 for i, c in enumerate(lowercase + digits + " "))
                 
    def to_morse(text):
        return "".join([morse[c] + " " for c in text if c in morse])
    
  6. Hand to apply, gravity The same?Amounts of website, for that.That brings with, by -. for structural Seattle SEO, results for optimizing the cheapest option.Limit Holdem is, ways a great.,

  7. Can ruin any, great self-esteem booster?Luxamore The Dewadaru, it is planned.Record of all, more interested when.Few are already Fuel Delivery Service, one is inferior a single track.Caulk leaking seams, glaucoma We have.,

  8. Sales They will, basis and imagine?A substantial online, line or perform.Back into play, aimed at all.Visit all the Seattle SEO, Customers who are vesiculosus at a.By consumers because, paint brush to.,

  9. Will be away, build a shared?The babys ears, auto policy and.Now practice making, to use online.Every length to Fuel Delivery Service, all different forms of existence come.The foreclosures are, how this album.,

  10. alexthelionx said
     
    class String
    	MORSE_CODE = [
    		['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', '--..'],
    		['1', '.----'],
    		['2', '..---'],
    		['3', '...--'],
    		['4', '....-'],
    		['5', '.....'],
    		['6', '-....'],
    		['7', '--...'],
    		['8', '---..'],
    		['9', '----.'],
    		['0', '-----'],
    	]
    
    	def to_morse
    		self.split(' ').collect { |word| _to_morse word }.join '  '
    	end
    
    	def to_ascii
    		self.split('  ').collect { |word| _to_ascii word }.join ' '
    	end
    
    private
    	def _to_morse string
    		string.downcase.split(//).collect do |letter| 
    			MORSE_CODE.select { |pair| pair[0].eql? letter }
    		end.map { |pair| pair[0][1] }.join(' ')
    	end
    
    	def _to_ascii string
    		string.downcase.split(' ').collect do |code| 
    			MORSE_CODE.select { |pair| pair[1].eql? code }
    		end.map { |pair| pair[0][0] }.join
    	end
    end
    
    message = 'Programming Praxis'
    puts message.to_morse
    puts message.to_morse.to_ascii
    

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 616 other followers

%d bloggers like this: