I am sending in an array of references and here is my subroutine:
sub multiply
{
my #product;
my $ref1 = $_[0];
my $ref2 = $_[1];
my #array1 = #$ref1;
my #array2 = #$ref2;
}
my ($rowsA,$columnsA) = &dimensions(#$ref1);
my ($rowsB,$columnsB) = &dimensions(#$ref2);
for $i (0..$rowsA-1){
for $j (0..$columnsB-1){
for $k (0..$columnsA-1){
$product[$i][$j] += $array1[$i][$k] * $array2[$k][$j];
}
}
}
This subroutine works for two parameters, but how do I multiply the matrices when more than two parameters are sent?
You want to use the #_ array directly. You won't be able to assign your references directly as you have in your example b/c as you have found, you don't know the number of items being passed. The good thing is, you don't need too. All arguments are passed to Perl sub-routines via the special #_ array. When you see my $x = shift; this array is where the data is being shifted from into your local variables. Here, since you can't know ahead of time how many vars to create, you can use the array directly. Typical array functions apply. (i.e. to get size you can use scalar(#_))
The following example displays the type of sub-routine you would write using regular scalars. It would be the same for references, you would just need to dereference them prior to their use.
#!/usr/bin/perl
my $result = multiply(2,4,5);
print "Result 1: $result\n";
$result = multiply(2,2);
print "Result 2: $result\n";
$result = multiply(2,2,3,5,6);
print "Result 3: $result\n";
sub multiply
{
my $ans = 1;
foreach my $x (#_) { $ans *= $x; }
return $ans;
}
To be combined with RC's answer, which explains what #_ is and how to use it, you can also recurse:
sub multiply {
return $_[0] * $_[1] if #_ <= 2;
return shift( #_ ) * multiply( #_ );
}
It won't be as fast as his answer, but for more complex tasks, can be easier to implement.
Related
I have created one array and pushing another array in each iteration of loop into it. Once it will be done I wanted to fetch the values of each sub array as well as array.
Code I am using :
my $i;
my #carrierValuesAll="";
while (my #row = $sth->fetchrow_array()) {
my #carrierValues=join(',', #row). "\n";
push(#carrierValuesAll, #carrierValues);
}
$len = #carrierValuesAll;
for ($i = 0; $i < $len; $i = $i + 1) {
print ("\#carrierValuesAll[$i] = $carrierValuesAll[$i]\n");
print("\n");
}
The output I am getting is :
#carrierValuesAll[1] = 1,1,https://au-sbc.trustidinc.com/tid,sbcLabStub,sbcLab,,SKY,0,2019-11-07 20:10:43,2021-07-02 04:39:43,TrustID Lab Oracle,Y,Y,Y,ivr.localdomain,Y,trustid
#carrierValuesAll[2] = 2,1,https://au-sbc.trustidinc.com/tid,sbcLab,sbcLab,,SKY,2,2019-11-07 20:10:43,2020-12-14 06:24:17,TrustID Lab Oracle,Y,Y,Y,ivr.localdomain,Y,admin
What I have tried :
$len = #carrierValuesAll;
for ($i = 0; $i < $len; $i = $i + 1) {
print ("\#carrierValuesAll[$i] = $carrierValuesAll[$i]\n");
print("\n");
for (my $j=0;$j<$carrierValuesAll[$i];$j++) {
print("\#carrierValuesAll[$j] = $carrierValuesAll[$j]\n");
print("\n");
}
}
added one more nested loop into it, but it wont helped.
This output is what coming from database & I am trying to store it in array.
You only have one array here - well you have a second array variable, but you only ever store a single, scalar value in it.
Let's look at your code:
my $i;
my #carrierValuesAll="";
Declaring $i so far away from where it is used is a bit of a code smell. It'll work fine, of course, but Perl gives you the flexibility to declare variables where you use them - so use it.
And #carrierValuesAll="" isn't doing what you think it's doing. I suspect you think it's declaring an empty array. But actually it creates an array containing a single element which is an empty string.
while (my #row = $sth->fetchrow_array()) {
my #carrierValues=join(',', #row). "\n";
push(#carrierValuesAll, #carrierValues);
}
You now declare a new array, #carrierValues, but you give it a value which is returned from join(). The whole point of join() is to take a list of values (your data in #row) and join it together into a single string. You then push that single string onto the end of #carrierValuesAll.
There is no point in storing the result from join() in an array. The result from join() is only ever a single, scalar value.
So after this loop has finished running, you have an array, #carrierValuesAll which contains one of these single strings for each of the rows in your database table. There are no "subarrays" here. Just an array of strings.
You then confirm that by printing the contents of #carrierValuesAll.
$len = #carrierValuesAll;
for ($i = 0; $i < $len; $i = $i + 1) {
print ("\#carrierValuesAll[$i] = $carrierValuesAll[$i]\n");
print("\n");
}
And that prints exactly what I would expect. You get the data that you read from the database separated by commas.
If you don't want a single string, then don't use join() to make one. Instead, take a copy of #row and store a reference to that in your array:
while (my #row = $sth->fetchrow_array()) {
push(#carrierValuesAll, [ #row ]);
}
I'm not sure what you want to do with your array of arrays, but here's how you could print it out:
$len = #carrierValuesAll;
for (my $i = 0; $i < $len; $i = $i + 1) {
print ("\#carrierValuesAll[$i] = #{$carrierValuesAll[$i]}\n");
print("\n");
}
Note that I've used #{$carrierValuesAll[$i]} to dereference the array reference stored at $carrierValuesAll[$i] and turn it back into an array which I can then print.
Note also that I've moved the declaration of $i into the for loop where it makes more sense.
But most Perl programmers wouldn't use this "C-style" for loop as it looks horrible and is harder to understand. I would write this code as:
for my $i (0 .. $#carrierValuesAll) {
...
}
Instead of calculating the length of the array and then remembering to stop one iteration before reaching that, I've used the special variable $#ARRAY which gives me the index of the last index in the array.
I have various subroutines that give me arrays of arrays. I have tested them separately and somehow when i write my main routine, I fail to make the program recognize my arrays. I know it's a problem of dereferencing, or at least i suspect it heavily.
The code is a bit long but I'll try to explain it:
my #leaderboard=#arrarraa; #an array of arrays
my $parentmass=$spect[$#spect]; #scalar
while (scalar #leaderboard>0) {
for my $i(0..(scalar #leaderboard-1)) {
my $curref=$leaderboard[$i]; #the program says here that there is an uninitialized value. But I start with a list of 18 elements.
my #currentarray=#$curref; #then i try to dereference the array
my $w=sumaarray (#currentarray);
if ($w==$parentmass) {
if (defined $Leader[0]) {
my $sc1=score (#currentarray);
my $sc2=score (#Leader);
if ($sc1>$sc2) {
#Leader=#currentarray;
}
}
else {#Leader=#currentarray;}
}
elsif ($w>$parentmass) {splice #leaderboard,$i,1;} #here i delete the element if it doesn't work. I hope it's done correctly.
}
my $leadref= cut (#leaderboard); #here i take the first 10 scores of the AoAs
#leaderboard = #$leadref;
my $leaderef=expand (#leaderboard); #then i expand the AoAs by one term
#leaderboard= #$leaderef; #and i should end with a completely different list to work with in the while loop
}
So I don't know how to dereference the AoAs correctly. The output of the program says:
"Use of uninitialized value $curref in concatenation (.) or string at C:\Algorithms\22cyclic\cyclospectrumsub.pl line 183.
Can't use an undefined value as an ARRAY reference at C:\Algorithms\22cyclic\cyclospectrumsub.pl line 184."
I would appreciate enormously any insight or recommendation.
The problem is with the splice that modifies the list while it is being processed. By using the 0..(scalar #leaderboard-1) you set up the range of elements to process at the beginning, but when some elements are removed by the splice, the list ends up shorter than that and once $i runs off the end of the modified list you get undefined references.
A quick fix would be to use
for (my $i = 0; $i < #leaderboard; $i++)
although that's neither very idiomatic nor efficient.
Note that doing something like $i < #leaderboard or #leaderboard-1 already provides scalar context for the array variable, so you don't need the scalar() call, it does nothing here.
I'd probably use something like
my #result;
while(my $elem = shift #leaderboard) {
...
if ($w==$parentmass) {
# do more stuff
push #result, $elem;
}
}
So instead of deleting from the original list, all elements would be taken off the original and only the successful (by whatever criterion) ones included in the result.
There seem to be two things going on here
You're removing all arrays from #leaderboard whose sumaarray is greater than $parentmass
You're putting in #Leader the array with the highest score of all the arrays in #leaderboard whose sumaarray is equal to $parentmass
I'm unclear whether that's correct. You don't seem to handle the case where sumaarray is less than $parentmass at all. But that can be written very simply by using grep together with the max_by function from the List::UtilsBy module
use List::UtilsBy 'max_by';
my $parentmass = $spect[-1];
my #leaderboard = grep { sumaarray(#$_) <= $parentmass } #arrarraa;
my $leader = max_by { score(#$_) }
grep { sumaarray(#$_) == $parentmass }
#leaderboard;
I'm sure this could be made a lot neater if I understood the intention of your algorithm; especially how those elements with a sumarray of less that $parentmass
I have two arrays:
#Array1 which contains A,B,C,D,E
and
#Array2 which contains L,B,C,F,E
I tried using a foreach to go through every element in the arrays and say if it's true or not to get started but I get dirty big fat errors when doing anything.
The program works perfectly with one foreach, but with a nested one it bugs out:
foreach my $var (#Array1){
print "Letter $var";
foreach my $var2 (#Array2){
if($var2 eq $var) {
print "They are equal";
} else {
next;
}
}
}
Any Ideas; one error is:
Use of uninitialized value in string eq at compare.pl line 192, <> line 2.
I've run it:
use strict;
use warnings;
my #Array1=qw/A B C D E/;
my #Array2=qw/L B C F E/;
foreach my $var (#Array1){
$\=$/;
print "Letter $var";
foreach my $var2 (#Array2){
if($var2 eq $var) {
print "They are equal";
} else {
next;
}
}
}
and I've got it
Letter A
Letter B
They are equal
Letter C
They are equal
Letter D
Letter E
They are equal
Your error means one of the values in either array is undefined. Since you do not show how the arrays are assigned values, it is impossible to tell why. It is clear that the problem lies in code you do not show.
What you can do to debug the problem is to print the arrays with the Data::Dumper module:
use Data::Dumper;
print Dumper \#Array1, \#Array2;
Then you should see what values they contain.
Also:
It is quite unnecessary to use your else { next } block, since it is the last part of the loop.
Printing "They are equal" would be quite a useless statement for bigger arrays, since you would get output that states that something is equal to something else, but you would only know one of the elements.
You might also want to use array_diff(#arr1, #arr2)
of Array::Utils.
As TLP says one of the values in the array is undef.
You can change the code to something like this to solve the problem.
foreach my $var (#Array1){
print "Letter $var \n";
foreach my $var2 (#Array2){
#(Both are undef (so they match)) OR (both are defined AND MATCH)
if((!defined($var2) && !defined($var)) ||
(defined($var2) && defined( $var) && ($var2 eq $var))) {
print "They are equal\n";
} else {
next;
}
}
}
I have a pointer to an array and either need to change the code considerably to make this work, or just figure out what the equivilent of the statement below is...
ptr = array;
*ptr++ = value;
So far I have most of it
$ptr = \#array;
$$ptr = $value;
but this doesn't increment the pointer. What do I do?
There are no pointers in Perl. What you have is:
my $ary_ref = \#array;
$ary_ref is now a reference to #array. You cannot dereference $ary_ref to get a scalar.
You can, however, iterate through the elements of #array in a variety of ways. For example, if you want, you can do:
#!/usr/bin/env perl
use strict; use warnings;
my #array;
my $ptr = sub {
my ($i, $ref, $val, $size) = (0, #_);
sub { $i < $size ? $ref->[$i ++ ] = $val : () }
}->(\#array, deadbeaf => 10);
1 while defined $ptr->();
use YAML;
print Dump \#array;
By the way, there is no reason you can't write
p[i] = value;
i++;
in C. In Perl, it might become:
$array[$_] = $value for 0 .. $#array;
or,
#array = ($value) x #array;
or,
$_ = value for #array;
etc. You should explain what the overall goal is rather than asking about a specific statement in C.
The C code modifies the first element of array and will likely change at least some part of the rest of the array.
#include <stdio.h>
int main(void)
{
int array[] = { 0, 0, 0, 0, 0 };
int value = 42;
int *ptr;
int i;
ptr = array;
*ptr++ = value;
for (i = 0; i < sizeof(array) / sizeof(array[0]); i++)
printf("%d ", array[i]);
puts("\n");
return 0;
}
Output:
42 0 0 0 0
Perl has references rather than pointers. References cannot be null, but there’s no reference arithmetic.
Modifying the first element of an array in Perl looks like
$ perl -wle '#a=(0)x5; $a[0] = 42; print "#a"'
42 0 0 0 0
or to be excessively literal
$ perl -wle '#a=(0)x5; $ptr = \$a[0]; $$ptr = 42; print "#a"'
42 0 0 0 0
Your question gives a single bookkeeping detail of the C code. What’s the broader context? What’s the C code doing?
In general, don’t write C in Perl. Considered broadly, C code tends to process arrays one item at a time, but Perl modifies the entire conceptual chunk, e.g., with a regex for strings or map or grep to transform entire arrays.
I'm trying to create an array of hashes, but I'm having trouble looping through the array. I have tried this code, but it does not work:
for ($i = 0; $i<#pattern; $i++){
while(($k, $v)= each $pattern[$i]){
debug(" $k: $v");
}
}
First, why aren't you useing strict and warnings? The following lines should be at the top of every Perl program you create, right after #!/usr/bin/perl. Always.
use strict;
use warnings;
And I know you aren't because I'm pretty sure you'd get some nice error messages out of strict and warnings from this, and from many other places in your code as well, judging by your variable use.
Second, why aren't you doing this:
for my $i (#pattern) {
..
}
That loops through every element in #pattern, assigning them to $i one at a time. Then, in your loop, when you want a particular element, just use $i. Changes to $i will be reflected in #pattern, and when the loop exits, $i will fall out of scope, essentially cleaning up after itself.
Third, for the love of Larry Wall, please declare your variables with my to localize them. It's really not that hard, and it makes you a better person, I promise.
Fourth, and last, your array stores references to hashes, not hashes. If they stored hashes, your code would be wrong because hashes start with %, not $. As it is, references (of any kind) are scalar values, and thus start with $. So we need to dereference them to get hashes:
for my $i (#pattern) {
while(my($k, $v) = each %{$i}) {
debug(" $k: $v");
}
}
Or, your way:
for (my $i = 0; $i<#pattern; $i++) { # added a my() for good measure
while(my($k, $v) = each %{$pattern[$i]}) {
debug(" $k: $v");
}
}
Try this instead:
for my $hashref (#pattern) {
for my $key (keys %$hashref) {
debug "$key: $hashref->{$key}";
}
}
The biggest problem with what you were trying was each $pattern[$i]. The each function expects a hash to work on, but $pattern[$i] returns a hashref (i.e. a reference to a hash). You could fix your code by dereferencing $pattern[$i] as a hash:
while(my($k, $v) = each %{$pattern[$i]}) {
Also, beware of the each function, it can leave the hash iterator in an incomplete state.
See the documentation for the perl data structures cookbook:
perldoc perldsc