Passing an Array of an Array to a subroutine using perl - arrays

Ok, so I got an array of an array (AoA) and I need to pass it to a subroutine, and then access it. This works… but is it strictly correct and indeed is there a better way that I should be doing this?
#!/usr/bin/perl
$a = "deep";
push(#b,$a);
push(#c,\#b);
print "c = #{$c[0]}\n";
&test(\#c);
sub test
{
$d = $_[0];
print "sub c = #{$$d[0]}\n";
}
Thanks

The definitely better way to do it is to use strict; and use warnings; and to declare your variables before using them.
Also, you know that is not good practice to name your vars a or b - give them meaningful names, especially because the $a variable for example, is defined by the compiler (the one used with sort {} #).

use strict; # Always!
use warnings; # Always!
sub test {
my ($d) = #_;
print "$d->[0][0]\n";
# Or to print all the deep elements.
for my $d2 (#$d) {
for (#$d2) {
print "$_\n";
}
}
}
{
my $a = "deep"; # or: my #c = [ [ "deep" ] ];
my #b;
my #c;
push(#b,$a);
push(#c,\#b);
test(\#c); # Can't pass arrays per say, just refs. You had this right.
}
Still needs better names. In particular, $a and $b should be avoided as they can interfere with sort.

Related

Extract number from array in Perl

I have a array which have certain elements. Each element have two char "BC" followed by a number
e.g - "BC6"
I want to extract the number which is present and store in a different array.
use strict;
use warnings;
use Scalar::Util qw(looks_like_number);
my #band = ("BC1", "BC3");
foreach my $elem(#band)
{
my #chars = split("", $elem);
foreach my $ele (#chars) {
looks_like_number($ele) ? 'push #band_array, $ele' : '';
}
}
After execution #band_array should contain (1,3)
Can someone please tell what I'm doing wrong? I am new to perl and still learning
To do this with a regular expression, you need a very simple pattern. /BC(\d)/ should be enough. The BC is literal. The () are a capture group. They save the match inside into a variable. The first group relates to $1 in Perl. The \d is a character group for digits. That's 0-9 (and others, but that's not relevant here).
In your program, it would look like this.
use strict;
use warnings;
use Data::Dumper;
my #band = ('BC1', 'BC2');
my #numbers;
foreach my $elem (#band) {
if ($elem =~ m/BC(\d)/) {
push #numbers, $1;
}
}
print Dumper #numbers;
This program prints:
$VAR1 = '1';
$VAR2 = '2';
Note that your code had several syntax errors. The main one is that you were using #band = [ ... ], which gives you an array that contains one array reference. But your program assumed there were strings in that array.
Just incase your naming contains characters other than BC this will exctract all numeric values from your list.
use strict;
use warnings;
my #band = ("AB1", "BC2", "CD3");
foreach my $str(#band) {
$str =~ s/[^0-9]//g;
print $str;
}
First, your array is an anonymous array reference; use () for a regular array.
Then, i would use grep to filter out the values into a new array
use strict;
use warnings;
my #band = ("BC1", "BC3");
my #band_array = grep {s/BC(\d+)/$1/} #band;
$"=" , "; # make printing of array nicer
print "#band_array\n"; # print array
grep works by passing each element of an array in the code in { } , just like a sub routine. $_ for each value in the array is passed. If the code returns true then the value of $_ after the passing placed in the new array.
In this case the s/// regex returns true if a substitution is made e.g., the regex must match. Here is link for more info on grep

How to use Perl `sort` and `pairwise` if I already have variables `$a` and `$b`

I want to write a Perl subroutine like this:
use List::MoreUtils qw{pairwise};
sub multiply {
my ($a, $b) = #_;
return [ pairwise {$a * $b} #$a, #$b ];
}
(multiplication is just an example, I'm gonna do something else)
However, this gives me nonsensical results, because Perl gets confused and tries to use the outer $a and $b instead of the list items.
If I try this code in a REPL (such as reply):
0> use List::MoreUtils qw{pairwise};
1> my $a = [1, 2, 3];
…
2> my $b = [3, 2, 1];
…
3> pairwise {$a * $b} #$a, #$b;
Can't use lexical $a or $b in pairwise code block at reply input line 1.
4> sort {$a <=> $b} #$a;
"my $a" used in sort comparison at reply input line 1.
"my $b" used in sort comparison at reply input line 1.
$res[2] = [
1,
3,
2
]
So far, my solution has been replacing my ($a, $b) = #_; with my ($a_, $b_) = #_; (i.e. renaming the troublesome variables).
Is there any other solution?
Well, first off - $a is an awful variable name. Why are you doing that? Single letters are almost always a bad choice, and just about ok if it's just an iterator or some other simple use.
So the solution would be 'don't call them $a and $b'.
I mean, if you really don't want to use a different name:
sub multiply {
return [ pairwise { $a * $b } #{$_[0]}, #{$_[1]} ];
}
But what you've done is really highlighted why namespace clashes are a bad idea, and thus using $a and $b in your code as actual variables is asking for trouble.
I don't think there's a way to make it work they way you're trying to, and even if you could - you'd end up with some code that's really confusing.
I mean, something like this should work:
sub multiply {
my ( $a, $b ) = #_;
return [ pairwise { $::a * $::b } #$a, #$b ];
}
Because then you're explicitly using the package $a and $b not the lexical one. But note - that won't work the same way if they're imported from another package, and it just generally gets messy.
But it's pretty filthy. perlvar tells you outright that you shouldn't do it:
$a
$b
Special package variables when using sort(), see sort. Because of this specialness $a and $b don't need to be declared (using use vars , or our()) even when using the strict 'vars' pragma. Don't lexicalize them with my $a or my $b if you want to be able to use them in the sort() comparison block or function.
And that's before you get into the territory of 'single letter variable names are pretty much always a bad idea'.
So seriously. Is:
my ( $first, $second ) = #_;
Actually so bad?
pairwise sets the $a and $b found in its caller's package, so you could use fully qualified variable names.
Assuming this code is in package main,
use List::MoreUtils qw{pairwise};
sub multiply {
my ($a, $b) = #_;
return [ pairwise { $main::a * $main::b } #$a, #$b ];
}
Alternatively, our creates a lexical variable that is aliased to the current package's variable with the same name, and it will override the my declaration since the most recent lexical-variable declaration wins out.
use List::MoreUtils qw{pairwise};
sub multiply {
my ($a, $b) = #_;
return [ pairwise { our $a * our $b } #$a, #$b ];
}
The second approach is obviously much less fragile than the first one, but you know what would be even less fragile? Not declaring $a and $b as lexical vars in the first place! :) It would be far simpler just to use different variables. Even $A and $B or $x and $y would be better.

Perl: Removing array items and resizing the array

I’m trying to filter an array of terms using another array in Perl. I have Perl 5.18.2 on OS X, though the behavior is the same if I use 5.010. Here’s my basic setup:
#!/usr/bin/perl
#use strict;
my #terms = ('alpha','beta test','gamma','delta quadrant','epsilon',
'zeta','eta','theta chi','one iota','kappa');
my #filters = ('beta','gamma','epsilon','iota');
foreach $filter (#filters) {
for my $ind (0 .. $#terms) {
if (grep { /$filter/ } $terms[$ind]) {
splice #terms,$ind,1;
}
}
}
This works to pull out the lines that match the various search terms, but the array length doesn’t change. If I write out the resulting #terms array, I get:
[alpha]
[delta quadrant]
[zeta]
[eta]
[theta chi]
[kappa]
[]
[]
[]
[]
As you might expect from that, printing scalar(#terms) gets a result of 10.
What I want is a resulting array of length 6, without the four blank items at the end. How do I get that result? And why isn’t the array shrinking, given that the perldoc page about splice says, “The array grows or shrinks as necessary.”?
(I’m not very fluent in Perl, so if you’re thinking “Why don’t you just...?”, it’s almost certainly because I don’t know about it or didn’t understand it when I heard about it.)
You can always regenerate the array minus things you don't want. grep acts as a filter allowing you to decide which elements you want and which you don't:
#!/usr/bin/perl
use strict;
my #terms = ('alpha','beta test','gamma','delta quadrant','epsilon',
'zeta','eta','theta chi','one iota','kappa');
my #filters = ('beta','gamma','epsilon','iota');
my %filter_exclusion = map { $_ => 1 } #filters;
my #filtered = grep { !$filter_exclusion{$_} } #terms;
print join(',', #filtered) . "\n";
It's pretty easy if you have a simple structure like %filter_exclusion on hand.
Update: If you want to allow arbitrary substring matches:
my $filter_exclusion = join '|', map quotemeta, #filters;
my #filtered = grep { !/$filter_exclusion/ } #terms;
To see what's going on, print the contents of the array in each step: When you splice the array, it shrinks, but your loop iterates over 0 .. $#terms, so at the end of the loop, $ind will point behind the end of the array. When you use grep { ... } $array[ $too_large ], Perl needs to alias the non-existent element to $_ inside the grep block, so it creates an undef element in the array.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my #terms = ('alpha', 'beta test', 'gamma', 'delta quadrant', 'epsilon',
'zeta', 'eta', 'theta chi', 'one iota', 'kappa');
my #filters = qw( beta gamma epsilon iota );
for my $filter (#filters) {
say $filter;
for my $ind (0 .. $#terms) {
if (grep { do {
no warnings 'uninitialized';
/$filter/
} } $terms[$ind]
) {
splice #terms, $ind, 1;
}
say "\t$ind\t", join ' ', map $_ || '-', #terms;
}
}
If you used $terms[$ind] =~ /$filter/ instead of grep, you'd still get uninitialized warnings, but as there's no need to alias the element, it won't be created.

shift an array from the values passed to subroutine in Perl

I am passing some undefined no. of arrays to the subroutine in perl I want to get these individual arrays in the subroutine and so I can run loop.
But as i was unable so I tried passing the count of arrays. But as we can remove individual elements from an array using shift can we do same with array i.e. is there some function similar to Shift for array.
sub iFrame
{
my $count=shift #_;
for (my $i=1;$i<=$count;$i++)
{
my #iFrame =#_; #need to remove this #iFrame each time
print qq[<iframe src="$iFrame[0]" width="$iFrame[1]"
height="$iFrame[2]" frameborder="$iFrame[3]" name="$iFrame[4]"></iframe>];
# and some other code
}
A better solution would be if I am able to do the same without passing the $count of arrays.
Best way is to pass a reference to the array, then dereference it in the subroutine. Like this:
use strict;
my #array = qw(a b c);
mysub(\#array);
sub mysub
{
my #array = #{$_[0]};
foreach (#array)
{
print $_
}
}
Pass them as references.
sub iFrame
{
for my $iFrame (#_)
{
print qq[<iframe src="$iFrame->[0]" width="$iFrame->[1]"
height="$iFrame->[2]" frameborder="$iFrame->[3]" name="$iFrame->[4]"></iframe>];
# and some other code
}
}
iFrame(
[ $src1, $width1, $height1, $frameborder1, $name1 ],
[ $src2, $width2, $height2, $frameborder2, $name2 ],
);

2d arr explicitpackage

I've looked through several threads on websites including this one to try and understand why I am getting an undeclared variable error for my usage of my $line . Each element of the #lines array is an array of strings.
The error is in line 25 and 27 with the $line[$count] statement
use strict;
use warnings;
my #lines;
my #sizes;
# read input from stdin file into 2d array
while(<>)
{
push(#lines, my #tokens = split(/\s+/, $_));
}
# search through each array for largest sizes in
# corresponding elements
for (my $count = 0; $count <= 5; $count++)
{
push(#sizes, 0);
foreach my $line (#lines)
{
if(length($line[$count])>$sizes[$count])
{
$sizes[$count] = length($line[$count]);
}
}
}
I can post the full code if it is necessary, but I am pretty sure the error must be in here somewhere.
The problem is here:
push(#lines, my #tokens = split(/\s+/, $_));
Pushing one array into another just adds all elements to the first array. So you are making a really long one dimensional array.
To fix this, use brackets to make an array reference:
push #lines, [ split(/\s+/, $_) ]; #No need for a temp variable.
Also, to access the array reference, you have to de-reference it. Both of these syntaxes are options:
${$line}[$count];
$line->[$count];
I think the second syntax is more readable.
Update: Also, you could simplify your code if you keep track of the longest lengths while you go through the file:
use strict;
use warnings;
use List::Util qw/max/;
my #lines;
my #sizes = (0)x6;
while(<>)
{
push #lines, [ my #tokens = split ];
#sizes = map { max ( length($tokens[$_]), $sizes[$_] ) } 0..$#tokens;
}
Note: The Data::Dumper core module is an invaluable tool when working with complex data structures in Perl.
use Data::Dumper;
print Dumper #lines;
This will print out the complete structure of whatever variable you give it. That way you can see if you actually created what you thought you did.

Resources