views:

6408

answers:

32

The challenge

The shortest code by character count, that will input a string using only alphabetical characters (upper and lower case), numbers, commas, periods and question mark, and returns a representation of the string in Morse code. The Morse code output should consist of a dash (-, ASCII 0x2D) for a long beep (AKA 'dah') and a dot (., ASCII 0x2E) for short beep (AKA 'dit').

Each letter should be separated by a space (' ', ASCII 0x20), and each word should be separated by a forward slash (/, ASCII 0x2F).

Morse code table:

alt text

Test cases:

Input:
    Hello world

Output:
    .... . .-.. .-.. --- / .-- --- .-. .-.. -..

Input:
    Hello, Stackoverflow.

Output:
    .... . .-.. .-.. --- --..-- / ... - .- -.-. -.- --- ...- . .-. ..-. .-.. --- .-- .-.-.-

Code count includes input/output (that is, the full program).

+40  A: 

Using a Morse Code Font?

Console.Write(params[0]);
johnc
I don't think the font uses 'ASCII' :)
LiraNuna
Oh well, just being lazy :)
johnc
If it contains a - and a . char, sure.
Dykam
Also, the program does not contain anything that uses the said font. That program will simply mirror the output.
LiraNuna
pfft, everyone's a critic
johnc
This is how I would've wanted to code it. :D awesome! (+1)
Dian
ROFLMAO this is the win for me if for nothing else then the laugh
Rune FS
+1: the right tool for the job
Dinah
+16  A: 

Python list comprehension, 159-character one-liner

for c in raw_input().upper():print c<","and"/"or bin(ord("•ƒwTaQIECBRZ^`šŒ#S#n|':<.$402&9/6)(18?,*%+3-;=>"[ord(c)-44])-34)[3:].translate(" "*47+"/.-"+" "*206),

Uses the similar data packing to P Daddy's C implementation, but does not store the bits in reverse order and uses bin() to extract the data rather than arithmetic. Note also that spaces are detected using inequality; it considers every character "less than comma" to be a space.

Python for loop, 205 chars including newlines

for a in raw_input().upper():
 q='_ETIANMSURWDKGOHVF_L_PJBXCYZQ__54_3___2__+____16=/_____7___8_90'.find(a);s=''
 while q>0:s='-.'[q%2]+s;q=~-q/2
 print['/','--..--','..--..','.-.-.-',''][' ,?.'.find(a)]+s,
ACoolie
+21  A: 

Perl, 170 characters (with a little help from accomplished golfer mauke). Wrapped for clarity; all newlines are removable.

$_=uc<>;y,. ,|/,;s/./$& /g;@m{A..Z,0..9,qw(| , ?)}=
".-D.NNN..]IN-NII..W-N-R.---.M-ANMAA.I.-].AIAA-NANMMIOMAOUMSMSASIB.MSOIONRKGWUD"
=~/../g;1while s![]\w|,?]!$m{$&}!g;print

Edit: now 186 characters. Minus two because I didn't need parens in the decoder, minus one for "1while", minus one for removing ending semicolon, minus one because vim was counting the ending newline, which isn't a significant part of the code.

Edit: 183 chars via obvious regex optimization.

mauke helped shave another 13 chars by tightening up regexes and the slice declare, and observing that the ] removal step could be folded into the general substitution step. He also pointed out that there's a spurious space at the end of the output; the best solution I have for that is to add s/ $//; before print, seven chars.

Explanation:

  1. Extract the morse dictionary. Each symbol is defined in terms of two chars, which can be either literal dots or dashes, or a reference to the value of another defined char. E and T contain dummy chars to avoid desyncing the decoder; we'll remove them later.
  2. Read and format the input. "Hello world" becomes "H E L L O / W O R L D"
  3. The next step depends on the input and output dictionaries being distinct, so turn dots in the input to an unused char (vertical bar, |)
  4. Replace any char in the input that occurs in the morse dictionary with its value in the dictionary, until no replacements occur.
  5. Remove the dummy char mentioned in step 1.
  6. Print the output.
hobbs
Man, StackOverflow really sucks at highlighting Perl...
Chris Lutz
True, but this code is bound to give almost any highlighter heartburn. :)
hobbs
I'll admit to not knowing Perl, but man... that kind of makes my head hurt to look at... Nice code!
Fiarr
@hobbs - Whatever side of the editor wars fence you may fall on, emacs cperl mode does a damn good job. It handles this, no sweat.
Chris Lutz
The goggles! They do nothing!
Dave Markle
Perl seems to have been designed explicitly to win these sort of competitions
kibibu
@Chris Lutz I use vim, which also highlights this correctly. Emacs, Vim, and Kate handle Perl well; most other things, including most web-based code formatters, get thrown by some of the more exotic quotelikes, or sometimes even regexes.@kibibu: There's a grain of truth in that. It's not literally true, but Perl's dual emphasis on flexibility and conciseness, when taken to obsessive levels, makes for good golfing. :)
hobbs
@hobbs - I usually use vim too, but emacs' cperl mode does so much more than vim that I can't use vim for Perl for very long. However, vim is also remarkably good about highlighting some of Perl's crazier constructs. But emacs highlights special characters in regexes, so I'm sold.
Chris Lutz
@kibibu - No, you're thinking J (http://en.wikipedia.org/wiki/J_%28programming_language%29).
Chris Lutz
I understood everything except the line where you initialize the hash...the one that starts with '@m{A..Z'. That just looks like black magic to me. Anybody care to explain?
Goose Bumper
@Goose Bumper: it's a hash slice assignment, `@hash{ keylist }` produces a list of (l)values corresponding to the `keylist`. The rest of the line is `"string" =~ m/regex/g` which produces a list of matches in the string. The ultimate effect is the same as `%m = (A => ".-", B => "D.", C => "NN")` etc. etc. only with a lot of characters saved.
hobbs
@hobbs: wow, that's very nicely done. Thank you for the explanation.
Goose Bumper
A: 

C, 533 characters

I took advice from some comments and switched to stdin. Killed another 70 characters roughly.

#include <stdio.h>
#include <ctype.h>
char *u[36] = {".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--..","-----",".----","..---","...--","....-",".....","-....","--...","---..","----."};
main(){
char*v;int x;char o;
do{
o = toupper(getc(stdin));v=0;if(o>=65&&o<=90)v=u[o-'A'];if(o>=48&&o<=57)v=u[o-'0'+26];if(o==46)v=".-.-.-";if(o==44)v="--..--";if(o==63)v="..--..";if(o==32)v="/";if(v)printf("%s ", v);} while (o != EOF);
}
Will Bickford
Wouldn't `char *u[36]` work and save 2 characters?
dmckee
I think reading from stdin would be much smaller than reading from the argument list.
strager
I think if you make the entire `u` one string and pad it so each entry is four characters, then special-case longer codes, you could reduce the character count by quite a bit too.
strager
good points thanks
Will Bickford
Since `toupper()` returns `int`, you can technically allow it to be implicitly declared and skip the `#include <ctype.h>` line. Bad practice in real code, but this is golf.
Chris Lutz
Also, `getchar()` is shorter than `getc(stdin)`.
Chris Lutz
Thanks for the tips.
Will Bickford
A: 

C89 (388 characters)

This is incomplete as it doesn't handle comma, fullstop, and query yet.

#define P putchar
char q[10],Q,tree[]=
"EISH54V 3UF    2ARL   + WP  J 1TNDB6=X/ KC  Y  MGZ7 Q  O 8  90";s2;e(x){q[Q++]
=x;}p(){for(;Q--;putchar(q[Q]));Q=0;}T(int x,char*t,int s){s2=s/2;return s?*t-x
?t[s2]-x?T(x,++t+s2,--s/2)?e(45):T(x,t,--s/2)?e(46):0:e(45):e(46):0;}main(c){
while((c=getchar())>=0){c-=c<123&&c>96?32:0;if(c==10)P(10);if(c==32)P(47);else
T(c,tree,sizeof(tree)),p();P(' ');}}

Wrapped for readability. Only two of the linebreaks are required (one for the #define, one after else, which could be a space). I've added a few non-standard characters but didn't add non-7-bit ones.

strager
+4  A: 

Python

Incomplete solution, but maybe somebody can make a full solution out of it. Doesn't handle digits or punctuation, but weighs in at only 154 chars.

def e(l):
 i='_etianmsurwdkgohvf_l_pjbxcyzq'.find(l.lower());v=''
 while i>0:v='-.'[i%2]+v;i=(i-1)/2;return v or '/'
def enc(s):return ' '.join(map(e,s))
Alec
finally someone who gets it. +1.
LiraNuna
I had a different approach but it still used a tree. This looks much more efficient, though!
strager
Now prove you're clever by extending it to handle the digits and punctuation without making the data 9 times as big :)
hobbs
I'm not sure what I "get" -- if I make it complete with this strategy, it's a lot longer than the Perl strategy. A lookup-tree instead of a lookup-table isn't exactly rocket science. I never claimed to be clever...
Alec
Adding `"__54_3___2__+____16=/_____7___8_90"` to `i` should make digits handled.
pingw33n
`return v or '/'` should go on the next line or it will be executed the first time it get into the while loop. Nice solution!
Andrea Ambu
Completed version: http://stackoverflow.com/questions/1352587/code-golf-morse-code/1353898#1353898I can't get it under 210 without changing enc's name :P
Andrea Ambu
A: 

C (381 characters)

char*p[36]={".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--..","-----",".----","..---","...--","....-",".....","-....","--...","---..","----."};
main(){int c;while((c=tolower(getchar()))!=10)printf("%s ",c==46?".-.-.-":c==44?"--..--":c==63?"..--..":c==32?"/":*(p+(c-97)));}
Andreas Grech
the numeric part is broken: you're missing a branch
Christoph
which branch ?
Andreas Grech
the one you're sitting on.
Alex
haha that hurts :P
Andreas Grech
A: 

C, 448 bytes using cmdline arguments:

char*a[]={".-.-.-","--..--","..--..","/",".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--..","-----",".----","..---","...--","....-",".....","-....","--...","---..","----."},*k=".,? ",*s,*p,x;main(int _,char**v){for(;s=*++v;putchar(10))for(;x=*s++;){p=strchr(k,x);printf("%s ",p?a[p-k]:isdigit(x)?a[x-18]:isalpha(x=toupper(x))?a[x-61]:0);}}

C, 416 bytes using stdin:

char*a[]={".-.-.-","--..--","..--..","/",".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--..","-----",".----","..---","...--","....-",".....","-....","--...","---..","----."},*k=".,? ",*p,x;main(){while((x=toupper(getchar()))-10){p=strchr(k,x);printf("%s ",p?a[p-k]:isdigit(x)?a[x-18]:isalpha(x)?a[x-61]:0);}}
Christoph
Spaces can be removed after #include.You can move your locals to the global scope to remove 'char'. Replace '\n' with 10. I think manually lowering the case of a string is smaller than using ctype (not sure).
strager
@strager: thx; I removed the includes completely as gcc and tcc will warn, but still compile
Christoph
+7  A: 

I was dorking around with a compact coding for the symbols, but I don't see if getting any better than the implicit trees already in use, so I present the coding here in case some one else can use it.

Consider the string:

 --..--..-.-.-..--...----.....-----.--/

which contains all the needed sequences as substrings. We could code the symbols by offset and length like this:

       ET  RRRIIGGGJJJJ    
--..--..-.-.-..--...----.....-----.--/
          CCCC  DD WWW       00000
,,,,,,   AALLLL BBBB        11111
--..--..-.-.-..--...----.....-----.--/
  ??????  KKK  MMSSS       22222   
        FFFF  PPPP        33333
--..--..-.-.-..--...----.....-----.--/
        UUU XXXX         44444       
          NN  PPPP  OOO 55555
--..--..-.-.-..--...----.....-----.--/
               ZZZZ    66666
                      77777      YYYY
--..--..-.-.-..--...----.....-----.--/
       ......        88888 HHHH
                    99999 VVVV  QQQQ
--..--..-.-.-..--...----.....-----.--/

with the space (i.e. word boundary) starting and ending on the final character (the '/'). Feel free to use it, if you see a good way.

Most of the shorter symbols have several possible codings, of course.


P Daddy found a shorter version of this trick (and I can now see at least some of the redundancy here) and did a nice c implementation. Alec did a python implementation with the first (buggy and incomplete) version. Hobbs did a pretty compact perl version that I don't understand at all.

dmckee
You forgot Y! :-) But I did make it into a neat little solution: http://stackoverflow.com/questions/1352587/code-golf-morse-code/1353214#1353214
Alec
D'oh! I was worried about that. I had to make the working string one longer to deal with it. Thanks.
dmckee
I based a solution on this: http://stackoverflow.com/questions/1352587/code-golf-morse-code/1356399#1356399
hobbs
A: 

The best I can do, in php, is: 615 characters, though I think the array of values could be handled far more efficiently, I just couldn't find a way to have the array use alphabetic, instead of numeric, keys without specifying them.

Still, to make it available for others to use (I think 'for laughing at' is a valid use...):

$alphabet = array(' '=>"/",'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'=>"--..",'0'=>"-----",'1'=>".----",'2'=>"..---",'3'=>"...--",'4'=>"....-",'5'=>".....",'6'=>"-....",'7'=>"--...",'8'=>"---..",'9'=>"----.",'.'=>".-.-.-",','=>"--..--",'?'=>"..--..");
$text = strtolower("hello world?");
$textArray = str_split($text);
foreach($textArray as $k=>$v) {echo $alphabet[$v] . " ";}

Bearing in mind I haven't included the opening/closing tags in the pasted code or the character count. I'm not sure if that's cheating, or not, but mentioned in the spirit of full disclosure.


Edited in response to Salaryman's comment, removed an unnecessary variable '$textArray' and shortened variable names, resulting in this (at 576 characters):

$a = array(' '=>"/",'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'=>"--..",'0'=>"-----",'1'=>".----",'2'=>"..---",'3'=>"...--",'4'=>"....-",'5'=>".....",'6'=>"-....",'7'=>"--...",'8'=>"---..",'9'=>"----.",'.'=>".-.-.-",','=>"--..--",'?'=>"..--..");
$t=str_split(strtolower("hello world?"));
foreach($textArray as $k=>$v) {echo $a[$v] . " ";}
David Thomas
You might want to use shorter variables names (one-lettered) and eliminate some variables, spaces, curly brackets and new lines.
Salaryman
@Salaryman: I...can't believe I never thought of that =)
David Thomas
Surely you could use numeric keys for the alphabet array, that would reduce size a lot.
Christian
@Christian, presumably, yeah. But...then I'd have to work out some other way of translating the required alphanumeric character into a number. Which isn't necessarily going to reduce the character size. Also: I'm not convinced I'm that good... =)
David Thomas
+1  A: 

C89 (293 characters)

Based off some of the other answers.

EDIT: Shrunk the tree (yay).

#define P putchar
char t['~']="~ETIANMSURWDKGOHVF~L~PJBXCYZQ~~54~3",o,q[9],Q=10;main(c){for(;Q;)t[
"&./7;=>KTr"[--Q]]="2167890?.,"[Q];while((c=getchar())>=0){c-=c<'{'&c>96?32:0;c-
10?c-32?0:P(47):P(10);for(o=1;o<'~';++o)if(t[o]==c){for(;o;o/=2)q[Q++]=45+(o--&1
);for(;Q;P(q[--Q]));break;}P(32);}}
strager
+3  A: 

Here's my contribution as a console application in VB.Net

Module MorseCodeConverter

 Dim M() As String = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", "-----", ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---..", "----."}

 Sub Main()
     Dim I, O
     Dim a, b

     While True
         I = Console.ReadLine()
         O = ""

         For Each a In I
             b = AscW(UCase(a))
             If b > 64 And b < 91 Then
                 O &= M(b - 65) & " "
             ElseIf b > 47 And b < 58 Then
                 O &= M(b - 22) & " "
             ElseIf b = 46 Then
                 O &= ".-.-.- "
             ElseIf b = 44 Then
                 O &= "--..-- "
             ElseIf b = 63 Then
                 O &= "..--.. "
             Else
                 O &= "/"
             End If

         Next

         Console.WriteLine(O)
     End While


 End Sub

End Module

I left he white space in to make it readable. Totals 1100 characters. It will read the input from the command line, one line at a time, and send the corresponding output back to the output stream. The compressed version is below, with only 632 characters.

Module Q
    Dim M() As String={".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--..","-----",".----","..---","...--","....-",".....","-....","--...","---..","----."}
    Sub Main()
        Dim I,O,a,b:While 1:I=Console.ReadLine():O="":For Each a In I:b=AscW(UCase(a)):If b>64 And b<91 Then:O &=M(b-65)&" ":ElseIf b>47 And b<58 Then:O &=M(b-22)&" ":ElseIf b=46 Then:O &=".-.-.- ":ElseIf b=44 Then:O &="--..-- ":ElseIf b=63 Then:O &= "..--.. ":Else:O &="/":End IF:Next:Console.WriteLine(O):End While
    End Sub
End Module
Kibbee
First step: remove all the unnecessary whitespace. Is this a code golf or what?
Konrad Rudolph
… that got it down to 583 characters although the Mono VB compiler no longer compiles it because it’s buggy and doesn’t know the `EndIf` command.
Konrad Rudolph
Yeah, I tried to get rid of a lot of extra whitespace, but VS.Net kept correcting my bad syntax, so I just kind of gave up. Does the Mono VB compiler interpret WEND as a proper replacement for End While? That could save an extra 5 characters.
Kibbee
removed all the extra white space I could, still can't get it down past 632
Kibbee
+2  A: 

Python 2; 171 characters

Basically the same as Andrea's solution, but as a complete program, and using stupid tricks to make it shorter.

for c in raw_input().lower():print"".join(".-"[int(d)]for d in bin(
('  etianmsurwdkgohvf_l_pjbxcyzq__54_3___2%7s16%7s7___8_90%12s?%8s.%29s,'
%(('',)*5)).find(c))[3:])or'/',

(the added newlines can all be removed)

Or, if you prefer not to use the bin() function in 2.6, we can get do it in 176:

for c in raw_input():C=lambda q:q>0and C(~-q/2)+'-.'[q%2]or'';print C(
(' etianmsurwdkgohvf_l_pjbxcyzq__54_3___2%7s16%7s7___8_90%12s?%8s.%29s,'%
(('',)*5)).find(c.lower()))or'/',

(again, the added newlines can all be removed)

Miles
+3  A: 

C (248 characters)

Another tree-based solution.

#define O putchar
char z[99],*t=
" ETINAMSDRGUKWOHBL~FCPJVX~YZQ~~54~3~~~2~~+~~~~16=/~~.~~7,~~8~90";c,p,i=0;
main(){gets(z);while(c=z[i++]){c-46?c-44?c:O(45):O(c);c=c>96?c-32:c;p=-1;
while(t[++p]!=c);for(;p;p/=2){O(45+p--%2);}c-32?O(32):(O(47),O(c));}}

Could be errors in source tree because wikipedia seems to have it wrong or maybe I misunderstood something.

pingw33n
Beats mine. Nice. I'll have to see what you did to make it shorter than mine. =]
strager
Why do you have `char` twice? Why not declare `z` and `t` with the same `char`, like you do for `c`, `p`, and `i`?
Chris Lutz
Yeah, missed that, thanks :)
pingw33n
You could #define O putchar( -- tht would save some characters
gnud
How would I use such macro with a number argument?
pingw33n
This prints garbage for '?', and any other character not in [A-Za-z0-9\.,].
P Daddy
@P Daddy - It can print all the garbage it wants for characters not in the specified requirements.
Chris Lutz
+1  A: 

Here's another approach, based on dmckee's work, demonstrating just how readable Python is:

Python

244 characters

def h(l):p=2*ord(l.upper())-88;a,n=map(ord,"AF__GF__]E\\E[EZEYEXEWEVEUETE__________CF__IBPDJDPBGAHDPC[DNBSDJCKDOBJBTCND`DKCQCHAHCZDSCLD??OD"[p:p+2]);return "--..--..-.-.-..--...----.....-----.-"[a-64:a+n-128]
def e(s):return ' '.join(map(h,s))

Limitations:

  • dmckee's string missed the 'Y' character, and I was too lazy to add it. I think you'd just have to change the "??" part, and add a "-" at the end of the second string literal
  • it doesn't put '/' between words; again, lazy

Since the rules called for fewest characters, not fewest bytes, you could make at least one of my lookup tables smaller (by half) if you were willing to go outside the printable ASCII characters.

EDIT: If I use naïvely-chosen Unicode chars but just keep them in escaped ASCII in the source file, it still gets a tad shorter because the decoder is simpler:

Python

240 characters

def h(l):a,n=divmod(ord(u'\x06_7_\xd0\xc9\xc2\xbb\xb4\xad\xa6\x9f\x98\x91_____\x14_AtJr2<s\xc1d\x89IQdH\x8ff\xe4Pz9;\xba\x88X_f'[ord(l.upper())-44]),7);return "--..--..-.-.-..--...----.....-----.-"[a:a+n]
def e(s):return ' '.join(map(h,s))

I think it also makes the intent of the program much clearer.

If you saved this as UTF-8, I believe the program would be down to 185 characters, making it the shortest complete Python solution, and second only to Perl. :-)

Alec
I've fixed my table to support both the Y *and* the word bounday by encoding spaces.
dmckee
+2  A: 

Python (210 characters)

This is a complete solution based on Alec's one

def e(l):
 i=(' etianmsurwdkgohvf_l_pjbxcyzq__54_3___2%7s16%7s7___8_90%12s?%8s.%29s,'%tuple('_'*5)).find(l.lower());v=''
 while i>0:v='-.'[i%2]+v;i=(i-1)/2
 return v or '/'
def enc(s):return ' '.join(map(e,s))
Andrea Ambu
+1: nice solution to the problem of making the data long enough to handle punctuation
Miles
+1  A: 

Here's a third, completely different way of encoding morse code:

Python

232 characters

def d(c):
 o='';b=ord("Y_j_?><80 !#'/_____f_\x06\x11\x15\x05\x02\x15\t\x1c\x06\x1e\r\x12\x07\x05\x0f\x16\x1b\n\x08\x03\r\x18\x0e\x19\x01\x13"[ord(c.upper())-44])
 while b!=1:o+='.-'[b&1];b/=2
 return o
e=lambda s:' '.join(map(d,s))

If you can figure out a way to map this onto some set of printable characters, you could save quite a few characters. This is probably my most direct solution, though I don't know if it's the most readable.

OK, now I've wasted way too much time on this.

Alec
+1  A: 

C, 338 chars

338 with indentation and all removable linebreaks removed:

#define O putchar
#define W while
char*l="x@@@@@ppmmmmm@@FBdYcbcbSd[Kcd`\31(\b1g_<qCN:_'|\25D$W[QH0";
int c,b,o;
main(){
  W(1){
    W(c<32)
      c=getchar()&127;
    W(c>96)
      c^=32;
    c-=32;
    o=l[c/2]-64;
    b=203+(c&1?o>>3:0);
    o=c&1?o&7:o>>3;
    W(o>6)
      O(47),o=0;
    c/=2;
    W(c--)
      b+=(l[c]-64&7)+(l[c]-64>>3);
    b=(((l[b/7]<<7)+l[b/7+1])<<(b%7))>>14-o;
    W(o--)
      O(b&(1<<o)?46:45);
    O(32);
  }
}

This isn't based on the tree approach other people have been taking. Instead, l first encodes the lengths of all bytes between 32 and 95 inclusive, two bytes to a character. As an example, D is -.. for a length of 3 and E is . for a length of 1. This is encoded as 011 and 001, giving 011001. To make more characters encodable and avoid escapes, 64 is then added to the total, giving 1011001 - 89, ASCII Y. Non-morse characters are assigned a length of 0. The second half of l (starting with \031) are the bits of the morse code itself, with a dot being 1 and a dash 0. To avoid going into high ASCII, this data is encoded 7 bits/byte.

The code first sanitises c, then works out the morse length of c (in o), then adds up the lengths of all the previous characters to produce b, the bit index into the data.

Finally, it loops through the bits, printing dots and dashes.

The length '7' is used as a special flag for printing a / when encountering a space.

There are probably some small gains to be had from removing brackets, but I'm way off from some of the better results and I'm hungry, so...

Jon Bright
+3  A: 

F#, 256 chars

let rec D i=if i=16 then" "else
 let x=int"U*:+F8c]uWjGbJ0-0Dnmd0BiC5?\4o`h7f>9[1E=pr_".[i]-32
 if x>43 then"-"+D(x-43)else"."+D x
let M(s:string)=s.ToUpper()|>Seq.fold(fun s c->s+match c with
|' '->"/ "|','->"--..-- "|'.'->".-.-.- "|_->D(int c-48))""

For example

M("Hello, Stack.") |> printfn "%s"

yields

.... . .-.. .-.. --- --..-- / ... - .- -.-. -.- .-.-.-

I think my technique may be unique so far. The idea is:

  • there is an ascii range of chars that covers most of what we want (0..Z)
  • there are only 43 chars in this range
  • thus we can encode one bit (dash or dot) plus a 'next character' in a range of 86 chars
  • the range ascii(32-117) is all 'printable' and can serve as this 86-char range
  • so the string literal encodes a table along those lines

There's a little more to it, but that's the gist. Comma, period, and space are not in the range 0..Z so they're handled specially by the 'match'. Some 'unused' characters in the range 0..Z (like ';') are used in the table as suffixes of other morse translations that aren't themselves morse 'letters'.

Brian
+3  A: 

C (233 characters)

W(n,p){while(n--)putch(".-.-.--.--..--..-.....-----..../"[p++]);}main(){
char*p,c,s[99];gets(s);for(p=s;*p;){c=*p++;c=toupper(c);c=c>90?35:c-32?
"È#À#¶µ´³²±°¹¸·#####Ê#@i Že‘J•aEAv„…`q!j“d‰ƒˆ"[c-44]:63;c-35?
W(c>>5,c&31):0;putch(0);}}

This takes input from stdin. Taking input from the command line adds 2 characters. Instead of:

...main(){char*p,c,s[99];gets(s);for(p=s;...

you get:

...main(int i,char**s){char*p,c;for(p=s[1];...

I'm using Windows-1252 code page for characters above 127, and I'm not sure how they'll turn up in other people's browsers. I notice that, in my browser at least (Google Chrome), two of the characters (between "@" and "i") aren't showing up. If you copy out of the browser and paste into a text editor, though, they do show up, albeit as little boxes.

It can be converted to ASCII-only, but this adds 24 characters, increasing the character count to 257. To do this, I first offset each character in the string by -64, minimizing the number of characters that are greater than 127. Then I substitute \xXX character escapes where necessary. It changes this:

...c>90?35:c-32?"È#À#¶µ´³²±°¹¸·#####Ê#@i Že‘J•aEAv„…`q!j“d‰ƒˆ"[c-44]:63;
c-35?W(...

to this:

...c>90?99:c-32?"\x88#\x80#vutsrqpyxw#####\x8A#\0PA)\xE0N%Q\nU!O\5\1\66DE 1
\xE1*S$ICH"[c-44]+64:63;c-99?W(...

Here's a more nicely formatted and commented version of the code:

/* writes `n` characters from internal string to stdout, starting with
 * index `p` */
W(n,p){
    while(n--)
        /* warning for using putch without declaring it */
        putch(".-.-.--.--..--..-.....-----..../"[p++]);

        /* dmckee noticed (http://tinyurl.com/n4eart) the overlap of the
         * various morse codes and created a 37-character-length string that
         * contained the morse code for every required character (except for
         * space).  You just have to know the start index and length of each
         * one.  With the same idea, I came up with this 32-character-length
         * string.  This not only saves 5 characters here, but means that I
         * can encode the start indexes with only 5 bits below.
         *
         * The start and length of each character are as follows:
         *
         *   A:  0,2    K:  1,3    U: 10,3    4: 18,5
         *   B: 16,4    L: 15,4    V: 19,4    5: 17,5
         *   C:  1,4    M:  5,2    W:  4,3    6: 16,5
         *   D:  9,3    N:  1,2    X:  9,4    7: 25,5
         *   E:  0,1    O: 22,3    Y:  3,4    8: 24,5
         *   F: 14,4    P:  4,4    Z:  8,4    9: 23,5
         *   G:  5,3    Q:  5,4    0: 22,5    .:  0,6
         *   H: 17,4    R:  0,3    1: 21,5    ,:  8,6
         *   I: 20,2    S: 17,3    2: 20,5    ?: 10,6
         *   J: 21,4    T:  1,1    3: 19,5
         */
}

main(){ /* yuck, but it compiles and runs */
    char *p, c, s[99];
    /* p is a pointer within the input string */
    /* c saves from having to do `*p` all the time */
    /* s is the buffer for the input string */

    gets(s); /* warning for use without declaring */

    for(p=s; *p;){ /* begin with start of input, go till null character */
        c = *p++; /* grab *p into c, increment p.
                   * incrementing p here instead of in the for loop saves
                   * one character */

        c=toupper(c); /* warning for use without declaring */

        c = c > 90 ? 35 : c - 32 ?
            "È#À#¶µ´³²±°¹¸·#####Ê#@i Že‘J•aEAv„…`q!j“d‰ƒˆ"[c - 44] : 63;

        /**** OR, for the ASCII version ****/

        c = c > 90 ? 99 : c - 32 ?
           "\x88#\x80#vutsrqpyxw#####\x8A#\0PA)\xE0N%Q\nU!O\5\1\66DE 1\xE1"
           "*S$ICH"[c - 44] + 64 : 63;

        /* Here's where it gets hairy.
         *
         * What I've done is encode the (start,length) values listed in the
         * comment in the W function into one byte per character.  The start
         * index is encoded in the low 5 bits, and the length is encoded in
         * the high 3 bits, so encoded_char = (char)(length << 5 | position).
         * For the longer, ASCII-only version, 64 is subtracted from the
         * encoded byte to reduce the necessity of costly \xXX representations.
         * 
         * The character array includes encoded bytes covering the entire range
         * of characters covered by the challenge, except for the space
         * character, which is checked for separately.  The covered range
         * starts with comma, and ends with capital Z (the call to `toupper`
         * above handles lowercase letters).  Any characters not supported are
         * represented by the "#" character, which is otherwise unused and is
         * explicitly checked for later.  Additionally, an explicit check is
         * done here for any character above 'Z', which is changed to the
         * equivalent of a "#" character.
         * 
         * The encoded byte is retrieved from this array using the value of
         * the current character minus 44 (since the first supported character
         * is ASCII 44 and index 0 in the array).  Finally, for the ASCII-only
         * version, the offset of 64 is added back in.
         */

        c - 35 ? W(c >> 5, c & 31) : 0;

        /**** OR, for the ASCII version ****/

        c - 99 ? W(c >> 5, c & 31) : 0;

        /* Here's that explicit check for the "#" character, which, as
         * mentioned above, is for characters which will be ignored, because
         * they aren't supported.  If c is 35 (or 99 for the ASCII version),
         * then the expression before the ? evaluates to 0, or false, so the
         * expression after the : is evaluated.  Otherwise, the expression
         * before the ? is non-zero, thus true, so the expression before
         * the : is evaluated.
         *
         * This is equivalent to:
         *
         *     if(c != 35) // or 99, for the ASCII version
         *         W(c >> 5, c & 31);
         *
         * but is shorter by 2 characters.
         */

        putch(0);
        /* This will output to the screen a blank space.  Technically, it's not
         * the same as a space character, but it looks like one, so I think I
         * can get away with it.  If a real space character is desired, this
         * must be changed to `putch(32);`, which adds one character to the
         * overall length.
    } /* end for loop, continue with the rest of the input string */
} /* end main */

This beats everything here except for a couple of the Python implementations. I keep thinking that it can't get any shorter, but then I find some way to shave off a few more characters. If anybody can find any more room for improvement, let me know.

EDIT:

I noticed that, although this routine rejects any invalid characters above ASCII 44 (outputting just a blank space for each one), it doesn't check for invalid characters below this value. To check for these adds 5 characters to the overall length, changing this:

...c>90?35:c-32?"...

to this:

...c-32?c>90|c<44?35:"...
P Daddy
Very nice. I wondered if there wasn't a more compact coding, but didn't see it as i was going along.
dmckee
a quicky: can you do `c=toupper(*p++)` instead of `c=*p++;c=toupper(c)`?
kibibu
@kibibu: Yup, that would remove 4 characters, although see my latest, http://stackoverflow.com/questions/1352587/code-golf-morse-code/1355594#1355594
P Daddy
+5  A: 

Python 3 One Liner: 172 characters

print(' '.join('/'if c==' 'else''.join('.'if x=='0'else'-'for x in bin(ord("ijÁĕÁÿïçãáàðøüþÁÁÁÁÁČÁÅ×ÚÌÂÒÎÐÄ×ÍÔÇÆÏÖÝÊÈÃÉÑËÙÛÜ"[ord(c)-44])-192)[3:])for c in input().upper()))

(Encoding the tranlation table into unicode code points. Works fine, and they display here fine in my test on my Windows Vista machine.)

Edited to pare down to 184 characters by removing some unnecessary spaces and brackets (making list comps gen exps).

Edit again: More spaces removed that I didn't even know was possible before seeing other answers here - so down to 176.

Edit again down to 172 (woo woo!) by using ' '.join instead of ''.join and doing the spaces separately. (duh!)

Anon
I don't have Py3K in front of me, but I believe you can save two characters by changing both instances of *x* `if` *y* `else` *z* to *y* `and` *x* `or` *z*, a third by changing space detection to an inequality (I used `c<','`), and a fourth by changing your data string to one which (like P Daddy's) only needs a two-digit number subtracted rather than a three-digit number.
Ben Blank
@Ben - Thanks, man. I really had to play with y and x or z a bit to see how it worked. Tested both that change and the inequality and verified that they do indeed work with Python 3. I was lazy with regard to the data string - didn't try hard enough to find an earlier one where everything printed. Rather than modify my answer for this one, I'll keep your tips in mind for next time. Very clever stuff. ;-)
Anon
+60  A: 

C (131 characters)

Yes, 131!

main(c){for(;c=c?c:(c=toupper(getch())-32)?
"•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"
[c-12]-34:-3;c/=2)putch(c/2?46-c%2:0);}

I eeked out a few more characters by combining the logic from the while and for loops into a single for loop, and by moving the declaration of the c variable into the main definition as an input parameter. This latter technique I borrowed from strager's answer to another challenge.


For those trying to verify the program with GCC or with ASCII-only editors, you may need the following, slightly longer version:

main(c){for(;c=c?c:(c=toupper(getchar())-32)?c<0?1:
"\x95#\x8CKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"
[c-12]-34:-3;c/=2)putchar(c/2?46-c%2:32);}

This version is 17 characters longer (weighing in at a comparatively huge 148), due to the following changes:

  • +4: getchar() and putchar() instead of the non-portable getch() and putch()
  • +6: escape codes for two of the characters instead of non-ASCII characters
  • +1: 32 instead of 0 for space character
  • +6: added "c<0?1:" to suppress garbage from characters less than ASCII 32 (namely, from '\n'). You'll still get garbage from any of !"#$%&'()*+[\]^_`{|}~, or anything above ASCII 126.

This should make the code completely portable. Compile with:

gcc -std=c89 -funsigned-char morse.c

The -std=c89 is optional. The -funsigned-char is necessary, though, or you will get garbage for comma and full stop.


135 characters

c;main(){while(c=toupper(getch()))for(c=c-32?
"•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"
[c-44]-34:-3;c;c/=2)putch(c/2?46-c%2:0);}

In my opinion, this latest version is much more visually appealing, too. And no, it's not portable, and it's no longer protected against out-of-bounds input. It also has a pretty bad UI, taking character-by-character input and converting it to Morse Code and having no exit condition (you have to hit Ctrl+Break). But portable, robust code with a nice UI wasn't a requirement.

A brief-as-possible explanation of the code follows:

main(c){
    while(c = toupper(getch())) /* well, *sort of* an exit condition */
        for(c =
            c - 32 ? // effectively: "if not space character"
            "•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5"[c - 44] - 34
            /* This array contains a binary representation of the Morse Code
             * for all characters between comma (ASCII 44) and capital Z.
             * The values are offset by 34 to make them all representable
             * without escape codes (as long as chars > 127 are allowed).
             * See explanation after code for encoding format.
             */
            : -3; /* if input char is space, c = -3
                   * this is chosen because -3 % 2 = -1 (and 46 - -1 = 47)
                   * and -3 / 2 / 2 = 0 (with integer truncation)
                   */
            c; /* continue loop while c != 0 */
            c /= 2) /* shift down to the next bit */
                putch(c / 2 ? /* this will be 0 if we're down to our guard bit */
                    46 - c % 2 /* We'll end up with 45 (-), 46 (.), or 47 (/).
                                * It's very convenient that the three characters
                                * we need for this exercise are all consecutive.
                                */
                    : 0 /* we're at the guard bit, output blank space */
                );
}

Each character in the long string in the code contains the encoded Morse Code for one text character. Each bit of the encoded character represents either a dash or a dot. A one represents a dash, and a zero represents a dot. The least significant bit represents the first dash or dot in the Morse Code. A final "guard" bit determines the length of the code. That is, the highest one bit in each encoded character represents end-of-code and is not printed. Without this guard bit, characters with trailing dots couldn't be printed correctly.

For instance, the letter 'L' is ".-.." in Morse Code. To represent this in binary, we need a 0, a 1, and two more 0s, starting with the least significant bit: 0010. Tack one more 1 on for a guard bit, and we have our encoded Morse Code: 10010, or decimal 18. Add the +34 offset to get 52, which is the ASCII value of the character '4'. So the encoded character array has a '4' as the 33rd character (index 32).

This technique is similar to that used to encode characters in ACoolie's, strager's(2), Miles's, pingw33n's, Alec's, and Andrea's solutions, but is slightly simpler, requiring only one operation per bit (shifting/dividing), rather than two (shifting/dividing and decrementing).

EDIT:
Reading through the rest of the implementations, I see that Alec and Anon came up with this encoding scheme—using the guard bit—before I did. Anon's solution is particularly interesting, using Python's bin function and stripping off the "0b" prefix and the guard bit with [3:], rather than looping, anding, and shifting, as Alec and I did.

As a bonus, this version also handles hyphen (-....-), slash (-..-.), colon (---...), semicolon (-.-.-.), equals (-...-), and at sign (.--.-.). As long as 8-bit characters are allowed, these characters require no extra code bytes to support. No more characters can be supported with this version without adding length to the code (unless there's Morse Codes for greater/less than signs).

Because I find the old implementations still interesting, and the text has some caveats applicable to this version, I've left the previous content of this post below.


Okay, presumably, the user interface can suck, right? So, borrowing from strager, I've replaced gets(), which provides buffered, echoed line input, with getch(), which provides unbuffered, unechoed character input. This means that every character you type gets translated immediately into Morse Code on the screen. Maybe that's cool. It no longer works with either stdin or a command-line argument, but it's pretty damn small.

I've kept the old code below, though, for reference. Here's the new.

New code, with bounds checking, 171 characters:

W(i){i?W(--i/2),putch(46-i%2):0;}c;main(){while(c=toupper(getch())-13)
c=c-19?c>77|c<31?0:W("œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"
[c-31]-42):putch(47),putch(0);}

Enter breaks the loop and exits the program.

New code, without bounds checking, 159 characters:

W(i){i?W(--i/2),putch(46-i%2):0;}c;main(){while(c=toupper(getch())-13)
c=c-19?W("œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"[c-31]-42):
putch(47),putch(0);}

Below follows the old 196/177 code, with some explanation:

W(i){i?W(--i/2),putch(46-i%2):0;}main(){char*p,c,s[99];gets(s);
for(p=s;*p;)c=*p++,c=toupper(c),c=c-32?c>90|c<44?0:W(
"œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"[c-44]-42):
putch(47),putch(0);}

This is based on Andrea's Python answer, using the same technique for generating the morse code as in that answer. But instead of storing the encodable characters one after another and finding their indexes, I stored the indexes one after another and look them up by character (similarly to my earlier answer). This prevents the long gaps near the end that caused problems for earlier implementors.

As before, I've used a character that's greater than 127. Converting it to ASCII-only adds 3 characters. The first character of the long string must be replaced with \x9C. The offset is necessary this time, otherwise a large number of characters are under 32, and must be represented with escape codes.

Also as before, processing a command-line argument instead of stdin adds 2 characters, and using a real space character between codes adds 1 character.

On the other hand, some of the other routines here don't deal with input outside the accepted range of [ ,.0-9\?A-Za-z]. If such handling were removed from this routine, then 19 characters could be removed, bringing the total down as low as 177 characters. But if this is done, and invalid input is fed to this program, it may crash and burn.

The code in this case could be:

W(i){i?W(--i/2),putch(46-i%2):0;}main(){char*p,s[99];gets(s);
for(p=s;*p;p++)*p=*p-32?W(
"œ*~*hXPLJIYaeg*****u*.AC5+;79-@6=0/8?F31,2:4BDE"
[toupper(*p)-44]-42):putch(47),putch(0);}
P Daddy
`getch()` is a nonstandard function, for the record. So is `putch()`, while we're at it.
Chris Lutz
Yeah, I know, but what's one more atrocity on top of the rest of this mess, right?
P Daddy
+1 I really really hope you never meet this in the wild... :)
Will Bickford
Wow. I never thought of using a guard bit. (I tried implementing with two-bit codes but that didn't help the size much due to requiring escape codes at times.) +1 if you code one in standard C89.
strager
I cannot get your code to compile OR work using GCC. It just sits there asking for input.
LiraNuna
I get an infinite loop printing garbage ... I'm guessing something is up with file encoding, but Vim isn't being too happy about converting. Can you please provide ASCII source (not using the MSB, a la latin-1)?
strager
@LiraNuna: Yeah, see my note about portability. For GCC, you'll probably have to use `getchar()` and `putchar()`. You may have trouble with the non-ASCII characters, too. You may be able to just re-enter them, though, using Alt+*xxx*. Let me know if you need to know what characters to re-enter and how.
P Daddy
@LiraNuna, strager: See edit for a more portable (slightly longer) version for verification.
P Daddy
@strager: Don't feed it piped input. Because it has no exit condition, you'll get the infinite loop garbage you're referring to. You have to start the program with no arguments and provide keyboard input.
P Daddy
With your latest gcc code, "Hello\n" outputs `......-...-..---/.///`
strager
@strager: It looks like that's a platform issue, not a compiler one. On my test system, gcc 3.4.6 on FreeSBIE, the portable code, given "Hello\n", outputs "`.... . .-.. .-.. --- ..- `". The final "`..-`" is the garbage from the `'\n'`, just like the "`/.///`" is the garbage you got from `'\n'`. But your platform didn't output spaces between the codes. I output ASCII 0 instead of 32 for spaces, which works on the two boxes I've tried it on, but not, apparently, on yours. I'll update the test code for this and do something about the garbage, too.
P Daddy
@strager: Okay, *completely* portable version. See update.
P Daddy
You, sir, are a beast. This is a beautiful, *awful* thing.
dmckee
This is pure win.
LiraNuna
You have non-ASCII characters in your source, so what charset are you using?
Ölbaum
I considered using bin(), but I was on a machine with only Python 2.5, and it's a 2.6 feature. :-)
Alec
@Ölbaum: I'm using Windows-1252. Sorry, I mentioned that in my other post using a different method (http://stackoverflow.com/questions/1352587/code-golf-morse-code/1354977#1354977), but didn't repeat the information here. Any encoding that will support 8-bit characters will work, though. The first three characters are \x95\x83\x8C, or 149,131,140 decimal.
P Daddy
Extremely cool.
Anon
You know what's really sad? I used this same algorithm for decoding morse from a byte in a piece of C code that I wrote in *2002*, but it didn't occur to me to use it here ;)
hobbs
@pzico: Clearing bit 4 would alter the non-alpha characters as well. The code currently handles a range of 47 characters, but clearing (or setting) bit 4 would force it to handle a range of 47 + 32 = 79 characters. Why 479, by the way? I don't have 9-bit characters. If you were going to `and` it, you would want to use 95.
P Daddy
You sir, are awesome!
M28
+2  A: 

C# Using Linq (133 chars)

    static void Main()
    {
        Console.WriteLine(String.Join(" ", (from c in Console.ReadLine().ToUpper().ToCharArray()
                                            select m[c]).ToArray()));
    }

OK, so I cheated. You also need to define a dictionary as follows (didn't bother counting the chars, since this blows me out of the game):

    static Dictionary<char, string> m = new Dictionary<char, string>() {
            {'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', "--.."},
            {'0', "-----"},
            {'1', ".----"},
            {'2', "..---"},
            {'3', "...--"},
            {'4', "....-"},
            {'5', "....."},
            {'6', "-...."},
            {'7', "--..."},
            {'8', "---.."},
            {'9', "----."},
            {' ', "/"},
            {'.', ".-.-.-"},
            {',', "--..--"},
            {'?', "..--.."},
        };

Still, can someone provide a more concise C# implementation which is also as easy to understand and maintain as this?

DSO
Readability and maintainability are the least of concerns in code golf. :)
Domenic
I posted a C# version that with the dictionary counted 266 chars.
Jader Dias
+2  A: 

Perl, 206 characters, using dmckee's idea

This is longer than the first one I submitted, but I still think it's interesting. And/or awful. I'm not sure yet. This makes use of dmckee's coding idea, plus a couple other good ideas that I saw around. Initially I thought that the "length/offset in a fixed string" thing couldn't come out to less data than the scheme in my other solution, which uses a fixed two bytes per char (and all printable bytes, at that). I did in fact manage to get the data down to considerably less (one byte per char, plus four bytes to store the 26-bit pattern we're indexing into) but the code to get it out again is longer, despite my best efforts to golf it. (Less complex, IMO, but longer anyway).

Anyway, 206 characters; newlines are removable except the first.

#!perl -lp
($a,@b)=unpack"b32C*",
"\264\202\317\0\31SF1\2I.T\33N/G\27\308XE0=\x002V7HMRfermlkjihgx\207\205";
$a=~y/01/-./;@m{A..Z,0..9,qw(. , ?)}=map{substr$a,$_%23,1+$_/23}@b;
$_=join' ',map$m{uc$_}||"/",/./g

Explanation:

  • There are two parts to the data. The first four bytes ("\264\202\317\0") represent 32 bits of morse code ("--.-..-.-.-----.....--..--------") although only the first 26 bits are used. This is the "reference string".
  • The remainder of the data string stores the starting position and length of substrings of the reference string that represent each character -- one byte per character, in the order (A, B, ... Z, 0, 1, ... 9, ".", ",", "?"). The values are coded as 23 * (length - 1) + pos, and the decoder reverses that. The last starting pos is of course 22.
  • So the unpack does half the work of extracting the data and the third line (as viewed here) does the rest, now we have a hash with $m{'a'} = '.-' et cetera, so all there is left is to match characters of the input, look them up in the hash, and format the output, which the last line does... with some help from the shebang, which tells perl to remove the newline on input, put lines of input in $_, and when the code completes running, write $_ back to output with newlines added again.
hobbs
+1  A: 

Haskell

type MorseCode = String

program :: String
program = "__5__4H___3VS__F___2 UI__L__+_ R__P___1JWAE"
     ++ "__6__=B__/_XD__C__YKN__7_Z__QG__8_ __9__0 OMT "

decode :: MorseCode -> String
decode = interpret program
    where
    interpret         = head . foldl exec []
    exec xs       '_' = undefined : xs
    exec (x:y:xs)  c  = branch    : xs
        where
        branch (' ':ds) = c : decode ds
        branch ('-':ds) = x ds
        branch ('.':ds) = y ds
        branch []       = [c]

For example, decode "-- --- .-. ... . -.-. --- -.. ." returns "MORSE CODE".

This program is from taken from the excellent article Fun with Morse Code.

namin
You're going the other way around - you're supposed to decode text into morse.
LiraNuna
You could generate random strings, decode them and exit when the decoded version matches the input string :)
gnibbler
+1  A: 

PHP

I modified the previous PHP entry to be slightly more efficient. :)

$a=array(32=>"/",44=>"--..--",1,".-.-.-",48=>"-----",".----","..---","...--","....-",".....","-....","--...","---..","----.",63=>"..--..",1,".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--..");
foreach(str_split(strtoupper("hello world?"))as$k=>$v){echo $a[ord($v)]." ";}

Komodo says 380 characters on 2 lines - the extra line is just for readability. ;D The interspersed 1s in the array is just to save 2 bytes by filling that array position with data instead of manually jumping to the array position after that.

Consider the first vs. the second. The difference is clearly visible. :)

array(20=>"data",22=>"more data";
array(20=>"data",1,"more data";

The end result, however, is exactly as long as you use the array positions rather than loop through the contents, which we don't do on this golf course.

End result: 578 characters, down to 380 (198 characters, or ~34.26% savings).

grimman
A: 

VBA/VB6 (576 chars)

Note: No Option Explicit

Function MC(S)
For I = 1 To Len(S): C = UCase(Mid(S, I, 1)): MC = MC & IIf(C = " ", "/", "") & IIf(InStr("AEFHIJLPRSUVW12345.?", C), ".", IIf(InStr("BCDGKMNOQTXYZ06789,", C), "-", "")) & IIf(InStr("BCDFHIKNSUVXY23456?", C), ".", IIf(InStr("AGJLMOPQRWZ01789,.", C), "-", "")) & IIf(InStr("BDGHLQRSVXZ34567,.", C), ".", IIf(InStr("CFJKOPUWY01289?", C), "-", "")) & IIf(InStr("CFHLPZ45678,", C), ".", IIf(InStr("JQVXY01239.?", C), "-", "")) & IIf(InStr("56789.?", C), ".", IIf(InStr("01234,", C), "-", "")) & IIf(C = "?", ".", IIf(InStr(".,", C), "-", "")) & " ": Next
End Function
DJ
+4  A: 

C# 266 chars

The 131 char C solution translated to C# yields 266 characters:

foreach(var i in Encoding.ASCII.GetBytes(args[0].ToUpper())){var c=(int)i;for(c=(c-32!=0)?Encoding.ASCII.GetBytes("•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5")[c-44]-34:-3;c!=0;c/=2)Console.Write(Encoding.ASCII.GetChars(new byte[]{(byte)((c/2!=0)?46-c%2:0)}));}

which is more readable as:

foreach (var i in Encoding.ASCII.GetBytes(args[0].ToUpper()))
{
    var c = (int)i;
    for (c = ((c - 32) != 0) ? Encoding.ASCII.GetBytes("•ƒŒKa`^ZRBCEIQiw#S#nx(37+$6-2&@/4)'18=,*%.:0;?5")[c - 44] - 34 : -3
        ; c != 0
        ; c /= 2)
        Console.Write(Encoding.ASCII.GetChars(new byte[] { (byte)((c / 2 != 0) ? 46 - c % 2 : 0) }));
}
Jader Dias
+3  A: 

Golfscript - 106 chars - NO FUNNY CHARS :)

newline at the end of the input is not supported, so use something like this

echo -n Hello, Stackoverflow| ../golfscript.rb morse.gs

' '/{{.32|"!etianmsurwdkgohvf!l!pjbxcyzq"?)"UsL?/'#! 08<>"@".,?0123456789"?=or
2base(;>{'.-'\=}%' '}%}%'/'*

Letters are a special case and converted to lowercase and ordered in their binary positions.
Everything else is done by a translation table

gnibbler
+2  A: 

REBOL (118 characters)

A roughly 10 years old implementation

foreach c ask""[l: index? find" etinamsdrgukwohblzfcpövxäqüyj"c while[l >= 2][prin pick"-."odd? l l: l / 2]prin" "]

Quoted from: http://www.rebol.com/oneliners.html

(no digits though and words are just separated by double spaces :/ ...)

onetom
+1  A: 

Bash, a script I wrote a while ago (time-stamp says last year) weighing in at a hefty 1661 characters. Just for fun really :)

#!/bin/sh
txt=''
res=''
if [ "$1" == '' ]; then
    read -se txt
else
    txt="$1"
fi;
len=$(echo "$txt" | wc -c)
k=1
while [ "$k" -lt "$len" ]; do
    case "$(expr substr "$txt" $k 1 | tr '[:upper:]' '[:lower:]')" in
        'e')    res="$res"'.' ;;
        't')    res="$res"'-' ;;
        'i')    res="$res"'..' ;;
        'a')    res="$res"'.-' ;;
        'n')    res="$res"'-.' ;;
        'm')    res="$res"'--' ;;
        's')    res="$res"'...' ;;
        'u')    res="$res"'..-' ;;
        'r')    res="$res"'.-.' ;;
        'w')    res="$res"'.--' ;;
        'd')    res="$res"'-..' ;;
        'k')    res="$res"'-.-' ;;
        'g')    res="$res"'--.' ;;
        'o')    res="$res"'---' ;;
        'h')    res="$res"'....' ;;
        'v')    res="$res"'...-' ;;
        'f')    res="$res"'..-.' ;;
        'l')    res="$res"'.-..' ;;
        'p')    res="$res"'.--.' ;;
        'j')    res="$res"'.---' ;;
        'b')    res="$res"'-...' ;;
        'x')    res="$res"'-..-' ;;
        'c')    res="$res"'-.-.' ;;
        'y')    res="$res"'-.--' ;;
        'z')    res="$res"'--..' ;;
        'q')    res="$res"'--.-' ;;
        '5')    res="$res"'.....' ;;
        '4')    res="$res"'....-' ;;
        '3')    res="$res"'...--' ;;
        '2')    res="$res"'..---' ;;
        '1')    res="$res"'.----' ;;
        '6')    res="$res"'-....' ;;
        '7')    res="$res"'--...' ;;
        '8')    res="$res"'---..' ;;
        '9')    res="$res"'----.' ;;
        '0')    res="$res"'-----' ;;
    esac;
    [ ! "$(expr substr "$txt" $k 1)" == " " ] && [ ! "$(expr substr "$txt" $(($k+1)) 1)" == ' ' ] && res="$res"' '
    k=$(($k+1))
done;
echo "$res"
Hiato
+2  A: 

J, 124 130 134 characters

'.- /'{~;2,~&.>(]`(<&3:)@.(a:=])"0)}.&,&#:&.></.40-~a.i.')}ggWOKIHX`dfggggggg-@B4*:68,?5</.7>E20+193ACD'{~0>.45-~a.i.toupper

J beats C! Awesome!

Usage:

   '.- /'{~;2,~&.>(]`(<&3:)@.(a:=])"0)}.&,&#:&.></.40-~a.i.')}ggWOKIHX`dfggggggg-@B4*:68,?5</.7>E20+193ACD'{~0>.45-~a.i.toupper 'Hello World'
.... . .-.. .-.. --- / .-- --- .-. .-.. -.. 

   '.- /'{~;2,~&.>(]`(<&3:)@.(a:=])"0)}.&,&#:&.></.40-~a.i.')}ggWOKIHX`dfggggggg-@B4*:68,?5</.7>E20+193ACD'{~0>.45-~a.i.toupper 'Hello, Stackoverflow.'
.... . .-.. .-.. --- .-.-.- / ... - .- -.-. -.- --- ...- . .-. ..-. .-.. --- .-- --..-- 
David