How to create dynamic arrays in perl - perl

I have a situation where input is in form of
$n followed by n lines having elements of n different arrays.
like
2
1 2 3
1 6
means i have 2 arrays with elements as 1,2,3 and 1,6.
Now i really dont know how big N can be. How to create dynamic arrays and store value to them. Arrays may be named array1,array2 or any other method to differentiate different arrays.
$n = <STDIN>;
for ($i = 0; $i < $n; $i++) {
$l = <STDIN>;
#arr = split(" ", $l);
}
Please improve this code.

You can use an array of arrays:
use strict;
my #array;
while(<STDIN>) {
my #line = split(" ", $_);
push #array, \#line;
}
# Just to display what's inside your array:
use Data::Dumper;
print Dumper(\#array);
Or even shorter:
use strict;
my #array;
push #array, [split ' ', $_] while(<STDIN>);

Related

Passing strings as array to subroutine and return count of specific char

I was trying to think in the right way to tackle this:
-I would to pass say, n elements array as argument to a subroutine. And for each element match two char types S and T and print for each element, the count of these letters. So far I did this but I am locked and found some infinite loops in my code.
use strict;
use warnings;
sub main {
my #array = #_;
while (#array) {
my $s = ($_ = tr/S//);
my $t = ($_ = tr/T//);
print "ST are in total $s + $t\n";
}
}
my #bunchOfdata = ("QQQRRRRSCCTTTS", "ZZZSTTKQSST", "ZBQLDKSSSS");
main(#bunchOfdata);
I would like the output to be:
Element 1 Counts of ST = 5
Element 2 Counts of ST = 6
Element 3 Counts of ST = 4
Any clue how to solve this?
while (#array) will be an infinite loop since #array never gets smaller. You can't read into the default variable $_ this way. For this to work, use for (#array) which will read the array items into $_ one at a time until all have been read.
The tr transliteration operator is the right tool for your task.
The code needed to get your results could be:
#!/usr/bin/perl
use strict;
use warnings;
my #data = ("QQQRRRRSCCTTTS", "ZZZSTTKQSST", "ZBQLDKSSSS");
my $i = 1;
for (#data) {
my $count = tr/ST//;
print "Element $i Counts of ST = $count\n";
$i++;
}
Also, note that my $count = tr/ST//; doesn't require the binding of the transliteration operator with $_. Perl assumes this when $_ holds the value to be counted here. Your code tried my $s = ($_ = tr/S//); which will give the results but the shorter way I've shown is the preferred way.
(Just noticed you had = instead of =~ in your statement. That is an error. Has to be $s = ($_ =~ tr/S//);)
You can combine the 2 sought letters as in my code. Its not necessary to do them separately.
I got the output you want.
Element 1 Counts of ST = 5
Element 2 Counts of ST = 6
Element 3 Counts of ST = 4
Also, you can't perform math operations in a quoted string like you had.
print "ST are in total $s + $t\n";
Instead, you would need to do:
print "ST are in total ", $s + $t, "\n";
where the operation is performed outside of the string.
Don't use while to traverse an array - your array gets no smaller, so the condition is always true and you get an infinite loop. You should use for (or foreach) instead.
for (#array) {
my $s = tr/S//; # No need for =~ as tr/// works on $_ by default
my $t = tr/T//;
print "ST are in total $s + $t\n";
}
Why tr///??
sub main {
my #array = #_;
while (#array) {
my $s = split(/S/, $_, -1) - 1;
my $t = split(/T/, $_, -1) - 1;
print "ST are in total $s + $t\n";
}
}

In Perl, how can I loop through variable names? [duplicate]

This question already has answers here:
How can I use a variable as a variable name in Perl?
(3 answers)
Closed 8 years ago.
I have N array and i want to print 1st elements of them in single for loop
my code:
#arr1=qw(1..5);
#arr2=qw(6..10);
...
#arrN=qw(...);
for($i=1;$i<=$N;$i++)
{
print "$arr[$i][0]";
}
When you find that you need to know the names of 0 .. N different variables. Its time to consider that you might be doing it wrong.
Arrays = list of 0 .. N values, can be sequential
Hash = list of 0 .. N named values
For your arrays, unless you actually want to be converting to strings, don't use qw() just use the bare ()
See solution below, you need an array of arrays:
#!/usr/bin/perl
use strict;
use warnings;
my $n = 10;
my #nArrays;
#fills the nArrays list with array_refs
for my $num(0 .. $n){
push #nArrays, [($num .. $num+5)];
}
#Print out the arrays, one per row, long way
for my $array (#nArrays){
print $array->[0], "\n";
}
If, contrary to all normal recommendations, you leave use strict; out of your script, you could use:
$N = 3;
#arr1 = (1..5);
#arr2 = (6..10);
#arr3 = (7..12);
#arrs = ("arr1", "arr2", "arr3");
for ($i = 0; $i < $N; $i++)
{
print "$arrs[$i][0]\n";
}
Output:
1
6
7
This is absolutely not recommended, but it does work still (mainly for reasons of backwards compatibility).
With use strict; etc, you can use explicit references:
use strict;
use warnings;
my $N = 3;
my #arr1 = (1..5);
my #arr2 = (6..10);
my #arr3 = (7..12);
my #arrs = (\#arr1, \#arr2, \#arr3);
for (my $i = 0; $i < $N; $i++)
{
print "$arrs[$i][0]\n";
}
Output:
1
6
7
On the whole, though, you would do better with a single array of arrays, perhaps like this:
use strict;
use warnings;
my $N = 3;
my #arr = ( [1..5], [6..10], [7..12] );
for (my $i = 0; $i < $N; $i++)
{
print "$arr[$i][0]\n";
}
Output:
1
6
7
What you have in mind is called a symbolic reference, and generally not a good idea.
If the values of these variables belong together, there is already a data structure that is indexed by an integer: Use an array to hold them:
use strict;
use warnings;
my #arr = (
[ 1 .. 5 ],
[ 6 .. 10 ],
# ...
[ 1_000_000 .. 1_000_005 ],
);
for my $i (0 .. $#arr) {
print $arr[$i][0], "\n";
}

$array can't print anything

This is my program , I want to let user type a matrix line by line and print the while matrix , but I can't see the matrix
The user will type
1 2 3
4 5 6
7 8 9
like this
and I want to let it show
1 2 3
4 5 6
7 8 9
Perl program
$Num = 3;
while($Num > 0 )
{
$Row = <STDIN>;
$Row = chomp($Row);
#Row_array = split(" ",$Row);
push #P_matrix , #Row_array;
#Row_array = ();
$Num = $Num - 1;
}
for($i=0;$i<scalar(#P_matrix);$i++)
{
for($j=0;$j<scalar(#P_matrix[$i]);$j++)
{
printf "$d ",$P_matrix[$i][$j];
}
print "\n";
}
I change the expression => printf "$d ",$P_matrix[$i][$j]; to print $P_matrix[$i][$j]
but still don't work.
To create a multi-dimensional array, you have to use references. Use
push #P_matrix, [ #Row_array ];
to create the desired structure.
Also, chomp does not return the modified string. Simply use
chomp $Row;
to remove a newline from $Row. Moreover, chomp is not needed at all if you split on ' '.
printf uses % as the formatting character, not $.
You can use Data::Dumper to inspect complex data structures. Use strict and warnings to help you avoid common problems. Here is how I would write your program:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
my #p_matrix;
push #p_matrix , [ split ' ' ] while <>;
warn Dumper \#p_matrix;
for my $i (0 .. $#p_matrix)
{
for my $j (0 .. $#{ $p_matrix[$i] })
{
printf '%d ', $p_matrix[$i][$j];
}
print "\n";
}
First and foremost please use use strict; use warnings;
Issues in your code:
You have a single dimensional array, but your are trying to access
it like two dimensional array. In order to make 2 dimensional array push the array reference of Row_array in #P_matrix as [#Row_array].
Where is $d defined? declare $d as my $d or our $d if you mean $d as scalar variable.
OR
For using %d, use need sprintf. Please read this.

Building and printing a multidimensional list in Perl without looping

The top answer in this post: How can I create a multidimensional array in Perl? suggests building a multi-dimensional array as follows:
my #array = ();
foreach my $i ( 0 .. 10 ) {
foreach my $j ( 0 .. 10 ) {
push #{ $array[$i] }, $j;
}
}
I am wondering if there is a way of building the array more compactly and avoiding the nested loop, e.g. using something like:
my #array = ();
my #other_array = (0 ... 10);
foreach my $i ( 0 .. 10 ) {
$array[$i] = #other_array; # This does not work in Perl
}
}
Does Perl support any syntax like that for building multi-dimensional arrays without nested looping?
Similarly, is there a way to print the multidimensional array without (nested) looping?
There is more than one way to do it:
Generating
push accepts LISTs
my #array;
push #{$array[$_]}, 0 .. 10 for 0 .. 10;
Alternative syntax:
my #array;
push #array, [ 0 .. 10 ] for 0 .. 10;
map eye-candy
my #array = map { [ 0 .. 10 ] } 0 .. 10;
Alternative syntax:
my #array = map [ 0 .. 10 ], 0 .. 10;
Printing
With minimal looping
print "#$_\n" for #array;
On Perl 5.10+
use feature 'say';
say "#$_" for #array;
With more formatting control
print join( ', ', #$_ ), "\n" for #array; # "0, 1, 2, ... 9, 10"
"No loops" (The loop is hidden from you)
use Data::Dump 'dd';
dd #array;
Data::Dumper
use Data::Dumper;
print Dumper \#array;
Have a look at perldoc perllol for more details
You are close, you need a reference to the other array
my #array; # don't need the empty list
my #other_array = (0 ... 10);
foreach my $i ( 0 .. 10 ) {
$array[$i] = \#other_array;
# or without a connection to the original
$array[$i] = [ #other_array ];
# or for a slice
$array[$i] = [ #other_array[1..$#other_array] ];
}
}
You can also make anonymous (unnamed) array reference directly using square braces [] around a list.
my #array;
foreach my $i ( 0 .. 10 ) {
$array[$i] = [0..10];
}
}
Edit: printing is probably easiest using the postfix for
print "#$_\n" for #array;
for numerical multidimensional arrays, you can use PDL. It has several constructors for different use cases. The one analogous to the above would be xvals. Note that PDL objects overload printing, so you can just print them out.
use PDL;
my $pdl = xvals(11, 11);
print $pdl;

In Perl, how can I iterate over multiple elements of an array?

I have a CSV file that I use split to parse into an array of N items, where N is a multiple of 3.
Is there a way i can do this
foreach my ( $a, $b, $c ) ( #d ) {}
similar to Python?
I addressed this issue in my module List::Gen on CPAN.
use List::Gen qw/by/;
for my $items (by 3 => #list) {
# do something with #$items which will contain 3 element slices of #list
# unlike natatime or other common solutions, the elements in #$items are
# aliased to #list, just like in a normal foreach loop
}
You could also import the mapn function, which is used by List::Gen to implement by:
use List::Gen qw/mapn/;
mapn {
# do something with the slices in #_
} 3 => #list;
You can use List::MoreUtils::natatime. From the docs:
my #x = ('a' .. 'g');
my $it = natatime 3, #x;
while (my #vals = $it->()) {
print "#vals\n";
}
natatime is implemented in XS so you should prefer it for efficiency. Just for illustration purposes, here is how one might implement a three element iterator generator in Perl:
#!/usr/bin/perl
use strict; use warnings;
my #v = ('a' .. 'z' );
my $it = make_3it(\#v);
while ( my #tuple = $it->() ) {
print "#tuple\n";
}
sub make_3it {
my ($arr) = #_;
{
my $lower = 0;
return sub {
return unless $lower < #$arr;
my $upper = $lower + 2;
#$arr > $upper or $upper = $#$arr;
my #ret = #$arr[$lower .. $upper];
$lower = $upper + 1;
return #ret;
}
}
}
my #list = (qw(one two three four five six seven eight nine));
while (my ($m, $n, $o) = splice (#list,0,3)) {
print "$m $n $o\n";
}
this outputs:
one two three
four five six
seven eight nine
#z=(1,2,3,4,5,6,7,8,9,0);
for( #tuple=splice(#z,0,3); #tuple; #tuple=splice(#z,0,3) )
{
print "$tuple[0] $tuple[1] $tuple[2]\n";
}
produces:
1 2 3
4 5 6
7 8 9
0
Not easily. You'd be better off making #d an array of three-element tuples, by pushing the elements onto the array as an array reference:
foreach my $line (<>)
push #d, [ split /,/, $line ];
(Except that you really ought to use one of the CSV modules from CPAN.
As of Perl v5.36 you can do exactly that:
foreach my ( $a, $b, $c ) ( #d ) { ... }
It's implemented as for_list experimental feature, so you can ignore the warning the usual way with use experimental qw(for_list);
For versions before v5.36 we'll rely on while/splice as mentioned above.