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';
}
}
}
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 join
ing 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With