I want to use an expression like
#{ %$hashref{'key_name'}[1]
or
%$hashref{'key_name}->[1]
to get - and then test - the second (index = 1) member of an array (reference) held by my hash as its "key_name" 's value. But, I can not.
This code here is correct (it works), but I would have liked to combine the two lines that I have marked into one single, efficient, perl-elegant line.
foreach my $tag ('doit', 'source', 'dest' ) {
my $exists = exists( $$thisSectionConfig{$tag});
my #tempA = %$thisSectionConfig{$tag} ; #this line
my $non0len = (#tempA[1] =~ /\w+/ ); # and this line
if ( !$exists || !$non0len) {
print STDERR "No complete \"$tag\" ... etc ... \n";
# program exit ...
}
I know you (the general 'you') can elegantly combine these two lines. Could someone tell me how I could do this?
This code it testing a section of a config file that has been read into a $thisSectionConfig reference-to-a-hash by Config::Simple. Each config file key=value pair then is (I looked with datadumper) held as a two-member array: [0] is the key, [1] is the value. The $tag 's are configuration settings that must be present in the config file sections being processed by this code snippet.
Thank you for any help.
You should read about Arrow operator(->). I guess you want something like this:
foreach my $tag ('doit', 'source', 'dest') {
if(exists $thisSectionConfig -> {$tag}){
my $non0len = ($thisSectionConfig -> {$tag} -> [1] =~ /(\w+)/) ;
}
else {
print STDERR "No complete \"$tag\" ... etc ... \n";
# program exit ...
}
I do not understand the role of qw in Perl, which I understood in in debugging the code in this thread.
Its manual is very limited here: qw/string/.
I am trying to understand what goes wrong in the following code I am doing simple manipulation to the data (multiplying by one (1.0)).
Original code which is a simplified version of the situation in a real-world case
use v5.16;
use Math::Geometry::Planar qw(SegmentLineIntersection);
sub x_intercepts {
my ($points) = #_;
die 'Must pass at least 2 points' unless #$points >= 2;
my #intercepts;
my #x_axis = ( [0, 2000], [1, 2000] );
foreach my $i (0 .. $#$points - 1) {
my $intersect = SegmentLineIntersection([#$points[$i,$i+1], #x_axis]);
push #intercepts, $intersect if $intersect;
}
return \#intercepts;
}
my #xs = qw/22.595451 20.089094 17.380813 15.091260 12.477935 10.054821 7.270003 4.804673 4.728526 4.619254 4.526920 4.418416 4.321419 4.219890 4.123336 4.009777 3.912648 3.804183 3.705847 3.597756 3.512301 3.393413 3.301963 3.196725 3.098560 3.007482 2.899825 2.801002 2.688680 2.598862 2.496139 2.393526 2.282183 2.190449 2.084530 1.987778 1.877562 1.788788 1.678473 1.578123 1.467071 1.373372 1.283629 1.176670 1.071805 0.975422 0.877622 0.767820 0.667409 0.562480 0.469068 0.354589 0.264291 0.152522 0.063765 -0.045323 -0.136783 -0.248559 -0.343694 -0.459178 -0.551917 -0.640803 -0.755502 -0.845535 -0.955227 -1.045879 -1.155600 -1.254556 -1.365163 -1.461669 -1.571370 -1.658043 -1.772672 -1.865942 -1.981037 -2.073702 -2.176205 -2.276184 -2.367371 -2.476278 -2.567385 -2.686326 -2.777433 -2.884357 -2.980067 -3.087754 -3.183607 -3.291003 -3.386942 -3.495822 -3.586759 -3.702955 -3.793550 -3.900680 -3.999672 -4.093094 -4.200047 -4.301026 -4.399742 -4.493190 -4.602013 -4.705124 -4.812383 -4.907510 -5.022904 -5.109829 -5.214304 -5.317662 -7.703172 -10.350131 -12.921361 -15.431203 -18.188013 -20.544248 -22.822808 -25.679854 -22.999092 -20.540434 -17.964916 -15.398857 -12.990375 -10.402209 -7.888263 -5.504909 -5.217892 -5.109841 -5.014187 -4.908558 -4.811807 -4.704282 -4.605555 -4.504613 -4.406546 -4.292540 -4.204043 -4.088770 -3.995524 -3.905669 -3.796544 -3.707958 -3.596449 -3.490966 -3.382869 -3.293054 -3.185821 -3.088417 -2.971214 -2.880314 -2.772518 -2.677986 -2.569794 -2.473668 -2.365096 -2.276422 -2.179232 -2.068195 -1.973110 -1.859565 -1.771873 -1.669422 -1.569953 -1.462626 -1.364911 -1.258100 -1.159318 -1.050486 -0.959979 -0.849149 -0.749421 -0.640950 -0.547122 -0.451754 -0.344463 -0.252269 -0.134625 -0.051640 0.052970 0.154112 0.266505 0.353926 0.468739 0.561666 0.673810 0.759169 0.881697 0.973207 1.082409 1.170424 1.282163 1.378096 1.472728 1.586454 1.678473 1.785688 1.873862 1.984090 2.086021 2.196799 2.292400 2.386097 2.493190 2.601726 2.694346 2.803450 2.901878 3.011959 3.103050 3.196979 3.294507 3.397563 3.504076 3.600163 3.712539 3.809573 3.919799 4.012314 4.120694 4.216406 4.322895 4.416466 4.522871 4.623917 4.735925 4.826929 7.361253 9.647208 12.337984 14.870260 17.439730 19.921717 22.524080 25.125903/;
# -1 1.0 3 4 5 6 7 7 8 9 10/;
my #ys = qw/3699.316162 3676.939697 3659.950195 3641.476318 3605.965576 3580.152588 3555.092529 3528.118408 3509.595703 3504.416504 3508.355957 3510.452881 3510.304443 3499.548340 3499.943848 3493.196533 3499.488770 3494.984375 3494.390137 3493.938965 3492.606689 3493.434814 3488.926514 3488.890869 3484.599854 3488.077393 3484.177979 3477.801758 3478.098877 3472.040039 3477.642090 3477.802002 3472.908447 3478.532715 3469.805420 3472.759766 3464.269043 3463.950684 3465.888184 3458.441650 3459.006104 3455.686035 3455.677490 3454.548828 3454.241211 3455.250000 3449.803711 3447.423340 3457.498779 3448.445557 3453.106689 3447.701172 3444.543945 3448.558350 3450.073730 3449.884033 3444.751953 3444.056152 3444.825195 3449.671143 2593.656494 788.985779 776.407776 776.385925 767.522522 774.794250 770.596008 775.218384 770.962769 766.214294 766.735962 759.847351 760.073486 760.026489 753.721741 755.601929 753.942566 758.356506 747.932617 746.332214 746.464844 747.055115 750.173706 737.463379 739.810486 742.011475 744.332581 743.022461 737.302490 737.396606 734.325256 737.305359 740.642395 734.709717 735.754089 737.961182 740.697510 727.310913 730.918640 728.325012 721.845459 728.389893 727.765625 729.961243 725.608459 723.581909 730.736084 720.707764 720.398193 691.499390 657.534546 628.854431 615.219727 572.711365 561.505127 539.865173 517.139709 545.076416 580.880005 602.851135 628.006104 657.119263 683.746033 692.044373 716.640320 715.451294 715.415405 718.968018 723.698669 729.758606 728.564514 734.523376 731.454468 736.899780 731.257263 729.390686 732.837463 730.479431 733.497803 735.362732 742.581543 742.998108 735.918579 738.190002 738.108337 738.154297 740.425537 739.496033 743.105835 737.412537 740.537354 747.498108 747.424194 748.992920 752.244263 755.603455 756.611755 761.916504 762.920715 752.261658 758.293823 755.664062 753.728882 756.746338 754.825684 755.080444 761.192383 761.955505 763.209351 760.402771 764.342224 775.400940 767.148621 775.184998 777.084595 778.357117 776.217163 1086.248291 3444.476807 3438.105957 3440.625000 3438.325195 3438.430420 3449.251709 3453.216309 3452.126465 3458.547119 3451.694336 3456.417725 3457.336426 3457.777832 3455.553955 3457.256348 3458.823486 3459.088623 3459.492187 3463.538818 3466.455078 3456.521240 3459.809082 3457.505127 3462.721191 3466.518066 3467.562744 3469.211182 3469.120361 3464.043945 3466.291992 3472.698486 3476.146729 3471.635254 3472.539551 3475.163574 3473.687744 3479.102051 3488.351807 3482.367432 3481.961914 3484.844238 3481.511719 3482.469238 3488.947021 3488.882080 3491.247314 3499.116699 3511.889893 3539.602783 3565.981445 3598.203613 3628.028076 3657.928955 3685.231689/;
# my #ys = 1.0 * #ys;
my #input_list ;
foreach my $i ( 0..$#ys ) {
push #input_list, [ $xs[$i], $ys[$i] ] ;
}
my $intercept_list = x_intercepts(\#input_list) ;
say join ",", #$_ for #$intercept_list ;
where I just activate this line # my #ys = 1.0 * #ys; by commenting out which gives
Must pass at least 2 points at test2.pl line 7.
which suggests that 1.0 * qw_stuff is doing something else than I expect.
I run
my #ys = 1 * scalar(#ys); my #ys = qw/#ys/;
but the same error persists.
How can you do the simple manipulation of the data between integer and qw in Perl?
Quote Words:
my #xs = qw/22.595451 20.089094 17.380813 15.091260 12.477935 10.054821 7.270003 4.804673 4.728526 4.619254 4.526920 4.418416 4.321419 4.219890 4.123336 4.009777 3.912648 3.804183 3.705847 3.597756 3.512301 3.393413 3.301963 3.196725 3.098560 3.007482 2.899825 2.801002 2.688680 2.598862 2.496139 2.393526 2.282183 2.190449 2.084530 1.987778 1.877562 1.788788 1.678473 1.578123 1.467071 1.373372 1.283629 1.176670 1.071805 0.975422 0.877622 0.767820 0.667409 0.562480 0.469068 0.354589 0.264291 0.152522 0.063765 -0.045323 -0.136783 -0.248559 -0.343694 -0.459178 -0.551917 -0.640803 -0.755502 -0.845535 -0.955227 -1.045879 -1.155600 -1.254556 -1.365163 -1.461669 -1.571370 -1.658043 -1.772672 -1.865942 -1.981037 -2.073702 -2.176205 -2.276184 -2.367371 -2.476278 -2.567385 -2.686326 -2.777433 -2.884357 -2.980067 -3.087754 -3.183607 -3.291003 -3.386942 -3.495822 -3.586759 -3.702955 -3.793550 -3.900680 -3.999672 -4.093094 -4.200047 -4.301026 -4.399742 -4.493190 -4.602013 -4.705124 -4.812383 -4.907510 -5.022904 -5.109829 -5.214304 -5.317662 -7.703172 -10.350131 -12.921361 -15.431203 -18.188013 -20.544248 -22.822808 -25.679854 -22.999092 -20.540434 -17.964916 -15.398857 -12.990375 -10.402209 -7.888263 -5.504909 -5.217892 -5.109841 -5.014187 -4.908558 -4.811807 -4.704282 -4.605555 -4.504613 -4.406546 -4.292540 -4.204043 -4.088770 -3.995524 -3.905669 -3.796544 -3.707958 -3.596449 -3.490966 -3.382869 -3.293054 -3.185821 -3.088417 -2.971214 -2.880314 -2.772518 -2.677986 -2.569794 -2.473668 -2.365096 -2.276422 -2.179232 -2.068195 -1.973110 -1.859565 -1.771873 -1.669422 -1.569953 -1.462626 -1.364911 -1.258100 -1.159318 -1.050486 -0.959979 -0.849149 -0.749421 -0.640950 -0.547122 -0.451754 -0.344463 -0.252269 -0.134625 -0.051640 0.052970 0.154112 0.266505 0.353926 0.468739 0.561666 0.673810 0.759169 0.881697 0.973207 1.082409 1.170424 1.282163 1.378096 1.472728 1.586454 1.678473 1.785688 1.873862 1.984090 2.086021 2.196799 2.292400 2.386097 2.493190 2.601726 2.694346 2.803450 2.901878 3.011959 3.103050 3.196979 3.294507 3.397563 3.504076 3.600163 3.712539 3.809573 3.919799 4.012314 4.120694 4.216406 4.322895 4.416466 4.522871 4.623917 4.735925 4.826929 7.361253 9.647208 12.337984 14.870260 17.439730 19.921717 22.524080 25.125903/;
is identical to
my #xs = ('22.595451', '20.089094', '17.380813', '15.091260', 'etc...');
but, as you can see saves a bunch of typing.
So now that you've got a bunch of strings, you can just use them as numbers and perl will automatically do the conversion for you (generally recommended), or if their stringatude offends you, you can explicitly do a premature manual conversion.
When you want to transform an array or list, think map or foreach.
#ys = map { 1 * $_ } #ys;
or
foreach my $element (#ys) {
$element *= 1; # or += 0 both work.
}
... but seriously, what is that actually buying you? If you allow Perl to do the conversion at it's leisure, the values will become dual valued and won't need to be re-converted if you use them as strings. With Manual conversion you risk ending up with a numeric only value that may require re-stringifying and thus a possible second bite at the round-off error apple.
The only time you may really want to do a manual conversion is when you need to convert to BigInt, BigFloat, or similar objects (i.e. you are concerned about any possible loss of precision an auto convert to a float will do); or if you are prepping the values for output via JSON or YAML to a system that cares about the difference between 'text representation of a number' (49) and 'quoted strings of digits' ("49"), though be warned that this counts as re-stringifying.
I'm trying to filter an array of a delimited text file in my program. The array from this text file looks like this:
YCL049C 1 511.2465 0 0 MFSK
YCL049C 2 4422.3098 0 0 YLVTASSLFVALT
YCL049C 3 1131.5600 0 0 DFYQVSFVK
YCL049C 4 1911.0213 0 0 SIAPAIVNSSVIFHDVSR
YCL049C 5 774.4059 0 0 GVAMGNVK
..
.
and the code I have for this section of the program is:
my #msfile_filtered;
my $msline;
foreach $msline (#msfile) {
my ($name, $pnum, $m2c, $charge, $missed, $sequence) = split (" ", $msline);
if (defined $amino) {
if ($amino =~ /$sequence/i) {
push (#msfile_filtered, $msline);
}
}
else {
push (#msfile_filtered, $msline);
}
}
$amino will just be a letter that will be input by the user, and corresponds to the last field $sequence. It is not essential that the user actually inputs $amino, so I need to duplicate this array and keep it unchanged if this is the case (hence the else statement). At the minute the #msfile_filtered array is empty, but I am unsure why, any ideas?
EDIT: just to clarify, there is only one space between each field, I copy and pasted this from notpad++, so extra spaced were added. The file itself will only have one space between fields.
Thanks in advance!
The regex that tries to find matching rows is backwards. To find a needle in a haystack, you need to write $haystack =~ /needle/, not the other way around.
Also, to simplify your logic, if $amino is undef, skip the loop entirely. I would rewrite your code as follows:
if (defined $amino)
{
foreach $msline (#msfile)
{
my ($name, $pnum, $m2c, $charge, $missed, $sequence) = split(" ", $msline);
push #msfile_filtered, $msline if ($sequence =~ /$amino/i);
}
} else
{
#msfile_filtered = #msfile;
}
You could simplify this further down to a single grep statement, but that begins to get hard to read. An example of such a line might be:
#msfile_filtered =
defined $amino
? grep { ( split(" ", $_ ) )[5] =~ /$amino/i } #msfile
: #msfile;
The split is should take more than one whitespaces, and the regex vars are vice versa.
First debug to check that values are correct after the split.
Also, you must swap your regex variables like this:
if ($sequence =~ /$amino/i) {
Now you're checking if $amino contains $sequence, which obviously it doesn't
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 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.
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.
Searching for values in hashes is a very intense job, you are better of by just using a reverse hash. You can easily reverse a hash with the reverse function in Perl. Also, while watching your code I have seen that you will be able to enter lower-case input. But while searching in hashes on keys, this is case-sensitive. So you will need to uppercase your input. Also, I do not really like the way to "end" an STDIN. An exit word/sign would be better and cleaner.
My take on your code
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 %reversemorse = reverse %morsecode;
print "Enter a message\n";
chomp (my $message = <STDIN>);
print &encode($message);
sub encode{
my $origmsg = shift(#_);
my #letters = split('',$origmsg);
my $morse = '';
foreach $l(#letters)
{
$morse .= $reversemorse{uc($l)}." ";
}
return $morse;
}