Perl array not saving all variables - arrays

I have a Perl program that's parsing data from an external program and saving it to an array. However, some of the data isn't being saved and comes back blank when trying to retrieve it later.
Here's the code parsing the data, which comes through as #packetData:
if(#packetData[0] ne ""){
if(!$detectedClient{"#packetData[0]"}) {
my $rawSignal = average(#packetData[2]);
my $distance = 10**((27.55-(20*logten(2437))+abs($rawSignal))/20);
my #newClient = ($rawSignal, # Signal (dBm)
1, # Count
#packetData[0], # Source MAC
time(), # Last seen
$distance); # Distance (m)
$detectedClient{"#packetData[0]"} = [#newClient];
$uniqueClient++;
print "++ New probe request from #packetData[0] [$rawSignal dBm, $distance m]\n";
} else {
$detectedClient{"#packetData[0]"}[1]++;
$detectedClient{"#packetData[0]"}[3] = time();
}
}
The print statement shows the signal and distance fine, but trying to show it from the detectedClient array gives with the code below gives a blank space:
for $key2 ( keys %detectedClient) {
#Signal, Count, MAC, Time
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($detectedClient{$key2}[3]);
my $lastSeen = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
print STDOUT sprintf("!! %-20s %10s %-20s\n", $detectedClient{$key2}[2], $detectedClient{$key2}[1], $lastSeen, $detectedClient{$key2}[0], $detectedClient{$key2}[4]);
}
Any ideas why this is happening?

The signal and the distance aren't printed because your pattern ("!! %-20s %10s %-20s\n") doesn't reference them (the 4th and 5th values). Fix:
printf("!! %-20s %5s %-20s %20s %20s\n",
$detectedClient{$key2}[2],
$detectedClient{$key2}[1],
$lastSeen,
$detectedClient{$key2}[0],
$detectedClient{$key2}[4],
);
Tested using the following: (Had to change for $key2 to for my $key2 too)
use strict;
use warnings qw( all );
use List::Util qw( sum );
sub average { sum(#{ $_[0] })/#{ $_[0] } }
sub logten { log($_[0])/log(10) }
my #packetData = ("foo", undef, [ 4, 5 ]);
my %detectedClient;
my $uniqueClient;
if(#packetData[0] ne ""){
if(!$detectedClient{"#packetData[0]"}) {
my $rawSignal = average(#packetData[2]);
my $distance = 10**((27.55-(20*logten(2437))+abs($rawSignal))/20);
my #newClient = ($rawSignal, # Signal (dBm)
1, # Count
#packetData[0], # Source MAC
time(), # Last seen
$distance); # Distance (m)
$detectedClient{"#packetData[0]"} = [#newClient];
$uniqueClient++;
print "++ New probe request from #packetData[0] [$rawSignal dBm, $distance m]\n";
} else {
$detectedClient{"#packetData[0]"}[1]++;
$detectedClient{"#packetData[0]"}[3] = time();
}
}
for my $key2 ( keys %detectedClient) {
#Signal, Count, MAC, Time
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($detectedClient{$key2}[3]);
my $lastSeen = sprintf("%04d/%02d/%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
printf("!! %-20s %5s %-20s %20s %20s\n",
$detectedClient{$key2}[2],
$detectedClient{$key2}[1],
$lastSeen, $detectedClient{$key2}[0],
$detectedClient{$key2}[4],
);
}
Output:
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 18.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 19.
Scalar value #packetData[2] better written as $packetData[2] at a.pl line 20.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 24.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 27.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 29.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 31.
Scalar value #packetData[0] better written as $packetData[0] at a.pl line 32.
++ New probe request from foo [4.5 dBm, 0.0164302613184467 m]
!! foo 1 2016/05/16 15:19:38 4.5 0.0164302613184467
Next time, please provide a minimal, runnable demonstration of the problem.

The syntax for an entire array is #array but to access individual elements, it is (e.g.) $array[0]
Also, there is a better way to write the inner if in your context.
Third, you're using an array reference for detectedClient, so the syntax could be changed from $detectedClient{$smac}[1]++ to $detectedClient{$smac}->[1]++ that would be clearer as to intent. I've changed it in the code below, and you'll could also change it when printing as well.
Here's your code modified to reflect all that and shortened to use a scalar:
my $smac = $packetData[0];
if ($smac ne "") {
if(! ref($detectedClient{$smac})) {
my $rawSignal = average($packetData[2]);
my $distance = 10**((27.55-(20*logten(2437))+abs($rawSignal))/20);
my #newClient = ($rawSignal, # Signal (dBm)
1, # Count
$smac, # Source MAC
time(), # Last seen
$distance); # Distance (m)
$detectedClient{$smac} = [#newClient];
$uniqueClient++;
print "++ New probe request from $smac [$rawSignal dBm, $distance m]\n";
} else {
my $ptr = $detectedClient{$smac};
$ptr->[1]++;
$ptr->[3] = time();
}
}
UPDATE:
Applying the use of a scalar and a bit of reindent, the print code looks like:
for $key2 ( keys %detectedClient) {
my $ptr = $detectedClient{$key2};
#Signal, Count, MAC, Time
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($ptr->[3]);
my $lastSeen = sprintf("%04d/%02d/%02d %02d:%02d:%02d",
$year+1900, $mon+1, $mday, $hour, $min, $sec);
print STDOUT sprintf("!! %-20s %10s %-20s\n",
$ptr->[2], $ptr->[1],
$lastSeen, $ptr->[0], $ptr->[4]);
}
Now, it's a little easier to see [for me, at least ;-)] that the format is short by two fields [so the last two arguments won't be printed].
So, something like this instead:
printf("!! %-20s %10s %-20s %10s %10s\n",

Related

perl - How to replace an empty value in an array without using a variable for comparison?

I have an array where each element comes from a line delimited by tab.
Initial code:
#!/usr/bin/perl -w
use strict;
The code below is a piece of the code.
sub parser_domains {
my #params = #_;
my $interpro_line = "";
my #interpro_vector = ( );
my $idr_sub_id = $params[0];
my $idr_sub_start = $params[1]+1;
my $idr_sub_end = $params[2]+1;
my $interpro_id = "";
my $interpro_start_location = 0;
my $interpro_end_location = 0;
my $interpro_db = "";
my $interpro_length = 0;
my $interpro_db_accession = "";
my $interpro_signature = "";
my $interpro_evalue = "";
my $interpro_vector_size = 0;
my $interpro_sub_file= "";
my $idr_sub_lenght = ($idr_sub_end-$idr_sub_start)+1;
$interpro_sub_file = "$result_directory_predictor/"."$idr_sub_id/"."$idr_sub_id".".fsa.tsv";
#open file; if it fails, print a error and exits.
unless( open(TSV_FILE_DATA, $interpro_sub_file) ) {
print "Cannot open file \"$interpro_sub_file\"\n\n";
return;
}
my #interpro_file_line = <TSV_FILE_DATA>;
close TSV_FILE_DATA;
foreach $interpro_line (#interpro_file_line) {
#interpro_vector = split('\t',$interpro_line);
$interpro_id = $interpro_vector[0];
$interpro_db = $interpro_vector[3];
$interpro_db_accession = $interpro_vector[4];
$interpro_start_location = $interpro_vector[6];
$interpro_end_location = $interpro_vector[7];
$interpro_signature = $interpro_vector[11];
$interpro_length = ($interpro_end_location-$interpro_start_location) + 1;
if ($interpro_signature eq ""){
$interpro_signature = "NOPIR";
printf IDP_REGION_FILE "\nFound a $interpro_db domain with no IPR: starts at $interpro_start_location and ends at $interpro_end_location\n";
printf IDP_REGION_FILE "The size of $interpro_db domain in the sequence is $interpro_length\n";
printf IDP_REGION_FILE "The IDR starts at $idr_sub_start and and ends at $idr_sub_end\n";
printf IDP_REGION_FILE "The size of IDR is $idr_sub_lenght\n";
domains_regions($idr_sub_start,$idr_sub_end,$interpro_start_location,$interpro_end_location,$interpro_signature,$interpro_length,$interpro_db,$idr_sub_id,$interpro_db_accession,$idr_sub_lenght);
}
else{
for $entry_line (#entry_file_line) {
#entry_vector = split('\t',$entry_line);
$entry_ac = $entry_vector[0];
$entry_type = $entry_vector[1];
$entry_name = $entry_vector[2];
chomp($entry_name);
if ($interpro_signature eq $entry_ac) {
printf IDP_REGION_FILE "\nFound a $interpro_db domain with Interpro Signature $entry_ac: starts at $interpro_start_location and ends at $interpro_end_location\n";
printf IDP_REGION_FILE "The size of $interpro_db domain in the sequence is $interpro_length\n";
printf IDP_REGION_FILE "The Interpro Signature $entry_ac belongs to type $entry_type\n";
printf IDP_REGION_FILE "The name of $entry_ac is $entry_name\n";
printf IDP_REGION_FILE "The IDR starts at $idr_sub_start and ends at $idr_sub_end\n";
printf IDP_REGION_FILE "The size of IDR is $idr_sub_lenght\n";
domains_regions($idr_sub_start,$idr_sub_end,$interpro_start_location,$interpro_end_location,$interpro_signature,$interpro_length,$interpro_db,$idr_sub_id,$interpro_db_accession,$idr_sub_lenght);
}
}
}
}
}
A example of tsv file (interproscan):
P51587 14086411a2cdf1c4cba63020e1622579 3418 Pfam PF09103 BRCA2, oligonucleotide/oligosaccharide-binding, domain 1 2670 2799 7.9E-43 T 15-03-2013
P51587 14086411a2cdf1c4cba63020e1622579 3418 ProSiteProfiles PS50138 BRCA2 repeat profile. 1002 1036 0.0 T 18-03-2013 IPR002093 BRCA2 repeat GO:0005515|GO:0006302
P51587 14086411a2cdf1c4cba63020e1622579 3418 Gene3D G3DSA:2.40.50.140 2966 3051 3.1E-52 T 15-03-2013
...
The scripts works perfectly, but the comparison $interpro_signature eq "" provides a warning.
Use of uninitialized value $interpro_signature in string eq at /home/duca/eclipse-workspace/idps/idp_parser_interpro.pl line 666.
So, I searched and tried manners to replace the empty value into the array before the comparison. I would like the empty value by "NOIPR".
I'm working with 9 completed genomes, and I have more than 324000 proteins to parse.
How can I replace the empty values in my array?
Thanks.
Your array may not have 12 elements (or the 12-th element may be undef)
my $interpro_signature = $interpro_vector[11] // 'some_default_value';
The // is the defined-or operator.
The error Use of uninitialized value means that the variable hasn't been initialized, or it's been set to undef.
See perldiag and use it regularly. Run code with perl -Mdiagnostics ... on errors, regularly.
The use warnings; is actually better than -w.
Update to a substantial edit of the question
From shown data it appears that yet other fields may not be given in the file; so proof all variables with defaults, much like for the array element at index 11 above. This is what you want to do in general anyway. For example, if there are all fields in the file but some may be empty (two tabs with nothing in between)
my #interpro_defaults = ('id_default', 'db_default', ...);
my ($interpro_id, $interpro_db, ...) =
map {
$interpro_vector[$_] // $interpro_defaults[$_]
} 0 .. $#interpro_defaults;
This relies on order (of variables) in the list, what can be error prone with variables; see below.
If some fields are simply not there there may be (far) more work to do.
There are too many separate variables, all related and named as $interpro_X (and then there are $idr_Y and $entry_Z, but fewer and perhaps manageable).
Can you not bundle them in a container-type variable or a data structure?
A hash %interpro seems suitable, with keys X (so, $interpro{id} etc). Then you can use them more easily and can perform some actions on the whole lot. You still have to watch for order when initializing since they are read sequentially, but it should be clearer this way. For example
my #interpro_vars = qw(id db db_accesssion ...);
my #interpro_vector = qw(id_default db_default ...);
my %interpro;
#interpro{#interpro_vars} = #interpro_vector;
# or simply
#interpro{qw(id db ...)} = qw(id_default db_default ...);
I've defined arrays with keys and values first and then used them, in case that you may want to later have those lists in arrays. If that's not the case you can initialize the hash with lists (the last line).
Here
my %h;
#h{LIST-keys} = LIST-values;
is a way to assign the list of LIST-values to the set of keys of the hash %h given in LIST-keys. They are assigned one for one, in the given order of both lists (which had better match in size). There is the # sigil in front of hash's keys since we are having a list (of keys) there, not a hash. Note that the hash must have been declared somewhere. See slices in perldata.
The problem is that your 3rd line contains only 9 elements. So
#interpro_vector = split('\t',$interpro_line);
for that line assigns only 9 elements to #interpro_vector but you then access $interpro_vector[11] (i.e. the 12th element) and that doesn't exist. You can now either check that #interpro_vector contains (at least) 12 elements:
if (#interpro_vector >= 12) {
...
}
Or you can use the defined-or operator as #zdim suggested to use a default value in case $interpro_vector[11] isn't defined:
$interpro_signature = $interpro_vector[11] // '';
The above line is equivalent to
if (defined $interpro_vector[11]) {
$interpro_signature = $interpro_vector[11];
} else {
$interpro_signature = '';
}
Now
if ($interpro_signature eq "") {
...
}
will work.

How to keep track of printed items in a for loop?

I was recently dealing with a hash that I wanted to print in a nice manner.
To simplify, it is just n array with two fields a['name']="me", a['age']=77 and the data I want to print like key1:value1,key2:value2,... and end with a new line. That is:
name=me,age=77
Since it is not an array whose indices are autoincremented values, I do not know how to loop through them and know when I am processing the last one.
This is important because it allows to use a different separator on the case I am in the last one. Like this, a different character can be printed in this case (new line) instead of the one that is printed after the rest of the files (comma).
I ended up using a counter to compare to the length of the array:
awk 'BEGIN {a["name"]="me"; a["age"]=77;
n = length(a);
for (i in a) {
count++;
printf "%s=%s%s", i, a[i], (count<n?",":ORS)
}
}'
This works well. However, is there any other better way to handle this? I don't like the fact of adding an extra count++.
In general when you know the end point of the loop you put the OFS or ORS after each field:
for (i=1; i<=n; i++) {
printf "%s%s", $i, (i<n?OFS:ORS)
}
but if you don't then you put the OFS before the second and subsequent fields and print the ORS after the loop:
for (idx in array) {
printf "%s%s", (++i>1?OFS:""), array[idx]
}
print ""
I do like the:
n = length(array)
for (idx in array) {
printf "%s%s", array[idx], (++i<n?OFS:ORS)
}
idea to get the end of the loop too, but length(array) is gawk-specific and the resulting code isn't any more concise or efficient than the 2nd loop above:
$ cat tst.awk
BEGIN {
OFS = ","
array["name"] = "me"
array["age"] = 77
for (idx in array) {
printf "%s%s=%s", (++i>1?OFS:""), array[idx], idx
}
print ""
}
vs
$ cat tst.awk
BEGIN {
OFS = ","
array["name"] = "me"
array["age"] = 77
n = length(array) # or non-gawk: for (idx in array) n++
for (idx in array) {
printf "%s=%s%s", array[idx], idx, (++i<n?OFS:ORS)
}
}

Perl Modification of non creatable array value attempted, subscript -1

I have a Perl-Script, which executes a recursive function. Within it compares two elements of a 2dimensional Array:
I call the routine with a 2D-Array "#data" and "0" as a starting value. First I load the parameters into a separate 2D-Array "#test"
Then I want to see, if the array contains only one Element --> Compare if the last Element == the first. And this is where the Error occurs: Modification of non creatable array value attempted, subscript -1.
You tried to make an array value spring into existence, and the subscript was probably negative, even counting from end of the array backwards.
This didn't help me much...I'm pretty sure it has to do with the if-clause "$counter-1". But I don't know what, hope you guys can help me!
routine(#data,0);
sub routine {
my #test #(2d-Array)
my $counter = $_[-1]
for(my $c=0; $_[$c] ne $_[-1]; $c++){
for (my $j=0; $j<13;$j++){ #Each element has 13 other elements
$test[$c][$j] = $_[$c][$j];
}
}
if ($test[$counter-1][1] eq $test[-1][1]{
$puffertime = $test[$counter][4];
}
else{
for (my $l=0; $l<=$counter;$l++){
$puffertime+= $test[$l][4]
}
}
}
#
#
#
if ($puffertime <90){
if($test[$counter][8]==0){
$counter++;
routine(#test,$counter);
}
else{ return (print"false");}
}
else{return (print "true");}
Weird thing is that I tried it out this morning, and it worked. After a short time of running he again came up with this error message. Might be that I didn't catch up a error constellation, which could happen by the dynamic database-entries.
Your routine() function would be easier to read if it starts off like this:
sub routine {
my #data = #_;
my $counter = pop(#data);
my #test;
for(my $c=0; $c <= $#data; $c++){
for (my $j=0; $j<13;$j++){ #Each element has 13 other elements
$test[$c][$j] = $data[$c][$j];
}
}
You can check to see if #data only has one element by doing scalar(#data) == 1 or $#data == 0. From your code snippet, I do not see why you need to copy the data to passed to routine() to #test. Seems superfluous. You can just as well skip all this copying if you are not going to modify any of the data passed to your routine.
Your next code might look like this:
if ($#test == 0) {
$puffertime = $test[0][4];
} else {
for (my $l=0; $l <= $counter; $l++) {
$puffertime += $test[$l][4];
}
}
But if your global variable $puffertime was initialized to zero then you can replace this code with:
for (my $l=0; $l <= $counter; $l++) {
$puffertime += $test[$l][4];
}

I can't access hash values

I have a program that creates an array of hashes while parsing a FASTA file. Here is my code
use strict;
use warnings;
my $docName = "A_gen.txt";
my $alleleCount = 0;
my $flag = 1;
my $tempSequence;
my #tempHeader;
my #arrayOfHashes = ();
my $fastaDoc = open(my $FH, '<', $docName);
my #fileArray = <$FH>;
for (my $i = 0; $i <= $#fileArray; $i++) {
if ($fileArray[$i] =~ m/>/) { # creates a header for the hashes
$flag = 0;
$fileArray[$i] =~ s/>//;
$alleleCount++;
#tempHeader = split / /, $fileArray[$i];
pop(#tempHeader); # removes the pointless bp
for (my $j = 0; $j <= scalar(#tempHeader)-1; $j++) {
print $tempHeader[$j];
if ($j < scalar(#tempHeader)-1) {
print " : "};
if ($j == scalar(#tempHeader) - 1) {
print "\n";
};
}
}
# push(#arrayOfHashes, "$i");
if ($fileArray[$i++] =~ m/>/) { # goes to next line
push(#arrayOfHashes, {
id => $tempHeader[0],
hla => $tempHeader[1],
bpCount => $tempHeader[2],
sequence => $tempSequence
});
print $arrayOfHashes[0]{id};
#tempHeader = ();
$tempSequence = "";
}
$i--; # puts i back to the current line
if ($flag == 1) {
$tempSequence = $tempSequence.$fileArray[$i];
}
}
print $arrayOfHashes[0]{id};
print "\n";
print $alleleCount."\n";
print $#fileArray +1;
My problem is when the line
print $arrayOfHashes[0]{id};
is called, I get an error that says
Use of uninitialized value in print at fasta_tie.pl line 47, line 6670.
You will see in the above code I commented out a line that says
push(#arrayOfHashes, "$i");
because I wanted to make sure that the hash works. Also the data prints correctly in the
desired formatting. Which looks like this
HLA:HLA00127 : A*74:01 : 2918
try to add
print "Array length:" . scalar(#arrayOfHashes) . "\n";
before
print $arrayOfHashes[0]{id};
So you can see, if you got some content in your variable. You can also use the module Data::Dumper to see the content.
use Data::Dumper;
print Dumper(\#arrayOfHashes);
Note the '\' before the array!
Output would be something like:
$VAR1 = [
{
'sequence' => 'tempSequence',
'hla' => 'hla',
'bpCount' => 'bpCount',
'id' => 'id'
}
];
But if there's a Module for Fasta, try to use this. You don't have to reinvent the wheel each time ;)
First you do this:
$fileArray[$i] =~ s/>//;
Then later you try to match like this:
$fileArray[$i++] =~ m/>/
You step through the file array, removing the first "greater than" sign in the line on each line. And then you want to match the current line by that same character. That would be okay if you only want to push the line if it has a second "greater than", but you will never push anything into the array if you only expect 1, or there turns out to be only one.
Your comment "puts i back to the current line" shows what you were trying to do, but if you only use it once, why not use the expression $i + 1?
Also, because you're incrementing it post-fix and not using it for anything, your increment has no effect. If $i==0 before, then $fileArray[$i++] still accesses $fileArray[0], only $i==1 after the expression has been evaluated--and to no effect--until being later decremented.
If you want to peek ahead, then it is better to use the pre-fix increment:
if ($fileArray[++$i] =~ m/>/) ...

Math::Complex screwing up my array references

I'm trying to optimize some code here, and wrote two different simple subroutines that will subtract one vector from another. I pass a pair of vectors to these subroutines and the subtraction is then performed. The first subroutine uses an intermediary variable to store the result whereas the second one does an inline operation using the '-=' operator. The full code is located at the bottom of this question.
When I use purely real numbers, the program works fine and there are no issues. However, if I am using complex operands, then the original vectors (the ones originally passed to the subroutines) are modified! Why does this program work fine for purely real numbers but do this sort of data modification when using complex numbers?
Note my process:
Generate random vectors (either real or complex depending on the commented out code)
Print the main vectors to the screen
Perform the first subroutine subtraction (using the third variable intermediary within the subroutine)
Print the main vectors to the screen again to prove that they have not changed, no matter the use of real or complex vectors
Perform the second subroutine subtraction (using the inline computation method)
Print the main vectors to the screen again, showing that #main_v1 has changed when using complex vectors, but will not change when using real vectors (#main_v2 is unaffected)
Print the final answers to the subtraction, which are always the correct answers, regardless of real or complex vectors
The issue arises because in the case of the second subroutine (which is quite a bit faster), I don't want the #main_v1 vector changed. I need that vector to do further calculations down the road, so I need it to stay the same.
Any idea on how to fix this, or what I'm doing wrong? My entire code is below, and should be functional. I've been using the CLI syntax shown below to run the program. I choose 5 just to keep everything easy for me to read.
c:\> bench.pl 5 REAL
or
c:\> bench.pl 5 IMAG
#!/usr/local/bin/perl
# when debugging: add -w option above
#
use strict;
use warnings;
use Benchmark qw (:all);
use Math::Complex;
use Math::Trig;
use Time::HiRes qw (gettimeofday);
system('cls');
my $dimension = $ARGV[0];
my $type = $ARGV[1];
if(!$dimension || !$type){
print "bench.pl <n> <REAL | IMAG>\n";
print " <n> indicates the dimension of the vector to generate\n";
print " <REAL | IMAG> dictates to use real or complex vectors\n";
exit(0);
}
my #main_v1;
my #main_v2;
my #vector_sum1;
my #vector_sum2;
for($a=1;$a<=$dimension;$a++){
my $r1 = sprintf("%.0f", 9*rand)+1;
my $r2 = sprintf("%.0f", 9*rand)+1;
my $i1 = sprintf("%.0f", 9*rand)+1;
my $i2 = sprintf("%.0f", 9*rand)+1;
if(uc($type) eq "IMAG"){
# Using complex vectors has the issue
$main_v1[$a] = cplx($r1,$i1);
$main_v2[$a] = cplx($r2,$i2);
}elsif(uc($type) eq "REAL"){
# Using real vectors shows no issue
$main_v1[$a] = $r1;
$main_v2[$a] = $r2;
}else {
print "bench.pl <n> <REAL | IMAG>\n";
print " <n> indicates the dimension of the vector to generate\n";
print " <REAL | IMAG> dictates to use real or complex vectors\n";
exit(0);
}
}
# cmpthese(-5, {
# v1 => sub {#vector_sum1 = vector_subtract(\#main_v1, \#main_v2)},
# v2 => sub {#vector_sum2 = vector_subtract_v2(\#main_v1, \#main_v2)},
# });
# print "\n";
print "main vectors as defined initially\n";
print_vector_matlab(#main_v1);
print_vector_matlab(#main_v2);
print "\n";
#vector_sum1 = vector_subtract(\#main_v1, \#main_v2);
print "main vectors after the subtraction using 3rd variable\n";
print_vector_matlab(#main_v1);
print_vector_matlab(#main_v2);
print "\n";
#vector_sum2 = vector_subtract_v2(\#main_v1, \#main_v2);
print "main vectors after the inline subtraction\n";
print_vector_matlab(#main_v1);
print_vector_matlab(#main_v2);
print "\n";
print "subtracted vectors from both subroutines\n";
print_vector_matlab(#vector_sum1);
print_vector_matlab(#vector_sum2);
sub vector_subtract {
# subroutine to subtract one [n x 1] vector from another
# result = vector1 - vector2
#
my #vector1 = #{$_[0]};
my #vector2 = #{$_[1]};
my #result;
my $row = 0;
my $dim1 = #vector1 - 1;
my $dim2 = #vector2 - 1;
if($dim1 != $dim2){
syswrite STDOUT, "ERROR: attempting to subtract vectors of mismatched dimensions\n";
exit;
}
for($row=1;$row<=$dim1;$row++){$result[$row] = $vector1[$row] - $vector2[$row]}
return(#result);
}
sub vector_subtract_v2 {
# subroutine to subtract one [n x 1] vector from another
# implements the inline subtraction method for alleged speedup
# result = vector1 - vector2
#
my #vector1 = #{$_[0]};
my #vector2 = #{$_[1]};
my $row = 0;
my $dim1 = #vector1 - 1;
my $dim2 = #vector2 - 1;
if($dim1 != $dim2){
syswrite STDOUT, "ERROR: attempting to subtract vectors of mismatched dimensions\n";
exit;
}
for($row=1;$row<=$dim1;$row++){$vector1[$row] -= $vector2[$row]} # subtract inline
return(#vector1);
}
sub print_vector_matlab { # for use with outputting square matrices only
my (#junk) = (#_);
my $dimension = #junk - 1;
print "V=[";
for($b=1;$b<=$dimension;$b++){
# $temp_real = sprintf("%.3f", Re($junk[$b][$c]));
# $temp_imag = sprintf("%.3f", Im($junk[$b][$c]));
# $temp_cplx = cplx($temp_real,$temp_imag);
print "$junk[$b];";
# print "$temp_cplx,";
}
print "];\n";
}
I've even tried modifying the second subroutine so that it has the following lines, and it STILL alters the #main_v1 vector when using complex numbers...I am completely confused as to what's going on.
#result = #vector1;
for($row=1;$row<=$dim1;$row++){$result[$row] -= $vector2[$row]}
return(#result);
and I've tried this too...still modifies #main_V1 with complex numbers
for($row-1;$row<=$dim1;$row++){$result[$row] = $vector1[$row]}
for($row=1;$row<=$dim1;$row++){$result[$row] -= $vector2[$row]}
return(#result);
Upgrade Math::Complex to at least version 1.57. As the changelog explains, one of the changes in that version was:
Add copy constructor and arrange for it to be called appropriately, problem found by David Madore and Alexandr Ciornii.
In Perl, an object is a blessed reference; so an array of Math::Complexes is an array of references. This is not true of real numbers, which are just ordinary scalars.
If you change this:
$vector1[$row] -= $vector2[$row]
to this:
$vector1[$row] = $vector1[$row] - $vector2[$row]
you'll be good to go: that will set $vector1[$row] to refer to a new object, rather than modifying the existing one.

Resources