Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Morse Code Decoder in Perl

Tags:

arrays

hash

perl

I am trying to teach myself Perl and I have been struggling... Last night I did a program to calculate the average of a set of numbers that the user provided in order to learn about lists and user input so today I thought I would do a Morse Code decoder to learn about Hashes. I have looked through the book that I bought and it doesn't really explain hashes very well... it actually doesn't explain a lot of things very well. Any help would be appreciated!

Anyways, I am wanting to write a program that decodes the morse code that the user inputs. So the user would enter:

-.-.
.-
-
...
!
.-.
..-
.-..
.

The exclamation point would signify a separate word. This message would return "Cats Rule" to the user. Below is the code I have so far... Remember.. I have been programming in perl for under 24 hours haha.

Code:

  use 5.010;




my %morsecode=(
    '.-'    =>'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',
    '.-.-.-'=>'.',  '--..--'=>',',  '---...'=>':',  '..--..'=>'?',
    '.----.'=>'\'', '-...-' =>'-',  '-..-.' =>'/',  '.-..-.'=>'\"'
);

my @k = keys %morsecode;
my @v = values %morsecode;

say "Enter a message in morse code separated by a line. Use the exclamation point (!) to separate words. Hit Control+D to signal the end of input.";
my @message = <STDIN>;
chomp @message;

my $decodedMessage = encode(@message);


sub encode {
    foreach @_ {
    if (@_ == @k) {
        return @k;

#This is where I am confused... I am going to have to add the values to an array, but I don't really know how to go about it.


        }
    else if(@_ == '!') {return ' '}
    else
    {
    return 'Input is not valid';
    }
    }
}
like image 429
JLott Avatar asked Dec 20 '22 10:12

JLott


2 Answers

Your code contains two syntactic errors: foreach requires a list to iterate over; this means parens. Unlike C and other languages, Perl doesn't support else if (...). Instead, use elsif (...).

Then there are a few semantic mistakes: The current value of an iteration is stored in $_. The array @_ contains the arguments of the call to your function.

Perl comparse strings and numbers differently:

Strings Numbers
eq      ==
lt      <
gt      >
le      <=
ge      >=
ne      !=
cmp     <=>

Use the correct operators for the task at hand, in this case, the stringy ones.

(Your code @_ == @k does something, namely using arrays in numeric context. This produces the number of elements, which is subsequenty compared. @_ == '!' is just weird.)


What you really want to do is to map the inputted values to a list of characters. Your hash defines this mapping, but we want to apply it. Perl has a map function, it works like

@out_list = map { ACTION } @in_list;

Inside the action block, the current value is available as $_.

We want our action to look up the appropriate value in the hash, or include an error message if there is no mapping for the input string:

my @letters = map { $morsecode{$_} // "<unknown code $_>" } @message;

This assumes ! is registered as a space in the morsecode hash.

We then make a single string of these letters by joining them with the empty string:

my $translated_message = join "", @letters;

And don't forget to print out the result!


The complete code:

#!/usr/bin/perl
use strict; use warnings; use 5.012;

my %morsecode=(
  '.-'    =>'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',
  '.-.-.-'=>'.',  '--..--'=>',',  '---...'=>':',  '..--..'=>'?',
  '.----.'=>'\'', '-...-' =>'-',  '-..-.' =>'/',  '.-..-.'=>'"',
  '!'     =>' ',
);

say "Please type in your morse message:";
my @codes = <>;
chomp @codes;

my $message = join "", map { $morsecode{$_} // "<unknown code $_>" } @codes;

say "You said:";
say $message;

This produces the desired output.

like image 173
amon Avatar answered Jan 08 '23 15:01

amon


There's a lot of value in learning the how and why, but here's the what:

sub encode {
  my $output;
  foreach my $symbol (@_) {
    my $letter = $morsecode{$symbol};
    die "Don't know how to decode $symbol" unless defined $letter;
    $output .= $letter
  }
  return $output;
}

or even as little as sub encode { join '', map $morsecode{$_}, @_ } if you're not too worried about error-checking. @k and @v aren't needed for anything.

like image 21
hobbs Avatar answered Jan 08 '23 15:01

hobbs