Printing "Multi-Dimensional" Array in Perl - arrays

I am having a problem attempting to print an array that contains arrays. When printing the array #dev which contains the other arrays, I am only managing to print the first three as it is indicated by the #printing in-line comments. The commented line #print($dev[4][2]); works fine, as well as any of the other combination of numbers within the allowable range. For some reason the for loop does not work. Help!?
my #dev;
my #tf;
my #a;
my #t;
my #f;
my #ofv;
my #tfv;
#tf = ('123456787', '123456788', '123456789'); #printing
#a = (78, 65, 57); #printing
#t = (70, 55, 42); #printing
#f = (77, 64, 56);
#ofv = ('true', 'false', 'false');
#tfv = ('false', 'true', 'true');
#dev = (
[#tf],
[#a],
[#t],
[#f],
[#ofv],
[#tfv],
);
#print($dev[4][2]);
for (my $i = 0; $i <= (scalar(#tf) - 1); $i++) {
for (my $j = 0; $j <= (scalar(#dev) - 1); $j++) {
print($dev[$i][$j]);
print("\n");
}
}
Thank you.

If you just want to show the data of such complex data struct, the modules Data::Dumper or Smart::Comments may be good options.
use Data::Dumper;
print Dumper(\#dev);
or
use Smart::Comments;
### #dev
The output is much more perl-style and not that readable, but is quite convenient to show the struct of such complex data.

Perl can be quite compact.
This snippet of code do the same thing for my arrays #arr1, #arr2 and #arr3:
#arr1 = (1..10);
#arr2 = ('a'..'j');
#arr3 = ('.') x 10;
#multid = \(#arr1, #arr2, #arr3);
print "#$_\n" for (#multid);
OUTPUT:
1 2 3 4 5 6 7 8 9 10
a b c d e f g h i j
. . . . . . . . . .
Also the [] copies an array and gives a reference to it (It's an anonymous array in memory, regardless of the array, a copy of which he is). If there is no need to such duplicate, it is better to use the backslash \ which instead gives a reference to existing array without coping. (like & operator in C, as tell us perldoc)

Your outermost for loop is constrained by the length of t, which is 3. It will never print more than three arrays.
If I understand what you're trying to do, you need top swap #t and #dev. That will print all your values.
That won't, however, print any array that is longer than 3 (the length of dev).
For that, you need:
#dev = (
[#tf], # Probably meant tf
[#a],
[#t],
[#f],
[#ofv],
[#tfv],
);
#print($dev[4][2]);
for (my $i = 0; $i < #dev; $i++) {
for (my $j = 0; $j < #{ $dev[$i] }; $j++) {
print($dev[$i][$j]);
print("\n");
}
}

Related

How to make an array loop indefinitely?

I have a list of names:
#names = qw(John Peter Michael);
I want to take 2 values from it, so I get John and Peter. If I want to take 2 more - I get Michael and John. 1 more - Peter. 3 more - Michael John and Peter, and so on.
I've started writing a subroutine where a global index ID would be set and remembered, and would reset itself to zero once it reaches scalar limit of an array, but then I read somewhere that Perl arrays "remember" the position they were looped.
Is that true or am I misunderstanding something? Is there a way to do my task an easy way?
It's not that hard to roll your own iterator, but perlfaq4 has your need covered:
How do I handle circular lists?
(contributed by brian d foy)
If you want to cycle through an array endlessly, you can increment the
index modulo the number of elements in the array:
my #array = qw( a b c );
my $i = 0;
while( 1 ) {
print $array[ $i++ % #array ], "\n";
last if $i > 20;
}
You can also use Tie::Cycle to use a scalar that always has the next element of the circular array:
use Tie::Cycle;
tie my $cycle, 'Tie::Cycle', [ qw( FFFFFF 000000 FFFF00 ) ];
print $cycle; # FFFFFF
print $cycle; # 000000
print $cycle; # FFFF00
The Array::Iterator::Circular creates an iterator object for circular arrays:
use Array::Iterator::Circular;
my $color_iterator = Array::Iterator::Circular->new(
qw(red green blue orange)
);
foreach ( 1 .. 20 ) {
print $color_iterator->next, "\n";
}
The roll-your-own variety
The subroutine is really quite simple (implemented as circularize in the code below). The value of $i is retained in $colors's scope, so no need for state variables:
sub circularize {
my #array = #_;
my $i = 0;
return sub { $array[ $i++ % #array ] }
}
my $colors = circularize( qw( red blue orange purple ) ); # Initialize
print $colors->(), "\n" for 1 .. 14; # Use
I never fully understood that mechanism (is it only on foreach?). I would just use state values, e.g.:
my #names = qw(John Peter Michael);
sub GetNames($) {
my $count = shift;
my #result = ();
state $index = 0;
state $length = scalar(#names);
while($count--) {
push(#result, $names[($index++ % $length)]);
}
return #result;
}
print join(", ", GetNames(2)), "\n\n";
print join(", ", GetNames(4)), "\n";
Outputs:
John, Peter
Michael, John, Peter, Michael

Referencing an element in a 2D array in Perl

I have the following code which reads in a 6x6 array from STDIN and saves it as an array of anonymous arrays. I am trying to print out each element with $arr[i][j], but the code below isn't working. It just prints out the first element over and over. How am I not accessing the element correctly?
#!/user/bin/perl
my $arr_i = 0;
my #arr = ();
while ($arr_i < 6){
my $arr_temp = <STDIN>;
my #arr_t = split / /, $arr_temp;
chomp #arr_t;
push #arr,\#arr_t;
$arr_i++;
}
foreach my $i (0..5){
foreach my $j (0..5){
print $arr[i][j] . "\n";
}
}
i and j are not the same as the variables you declared in the foreach lines. Change:
print $arr[i][j] . "\n";
to:
print $arr[$i][$j] . "\n";
warnings alerted me to this issue. You should add these lines to all your Perl code:
use warnings;
use strict;
To demonstrate the Perlish mantra that there's "more than one way to do it":
use 5.10.0; # so can use "say"
use strict;
use warnings qw(all);
sub get_data {
my ($cols, $rows) = #_;
my ($line, #rows);
my $i;
for ($i = 1; $i <= $rows and $line = <DATA>; $i++) {
chomp $line;
my $cells = [ split ' ', $line ];
die "Row $i had ", scalar(#$cells), " instead of $cols" if #$cells != $cols;
push #rows, $cells;
}
die "Not enough rows, got ", $i - 1, "\n" if $i != $rows + 1;
\#rows;
}
sub print_data {
my ($cols, $rows, $data) = #_;
for (my $i = 0; $i < $rows; $i++) {
for (my $j = 0; $j < $cols; $j++) {
say $data->[$i][$j];
}
}
}
my $data = get_data(6, 6);
print_data(6, 6, $data);
__DATA__
1 2 3 4 5 6
a b c d e f
6 5 4 3 2 1
f e d c b a
A B C D E F
7 8 9 10 11 12
Explanation:
if we use say, that avoids unsightly print ..., "\n"
get_data is a function that can be called and/or reused, instead of just being part of the main script
get_data knows what data-shape it expects and throws an error if it doesn't get it
[ ... ] creates an anonymous array and returns a reference to it
get_data returns an array-reference so data isn't copied
print_data is a function too
both functions use a conventional for loop instead of making lists of numbers, which in Perl 5 needs to allocate memory
There is also a two-line version of the program (with surrounding bits, and test data):
use 5.10.0; # so can use "say"
my #lines = map { [ split ' ', <DATA> ] } (1..6);
map { say join ' ', map qq{"$_"}, #$_ } #lines;
__DATA__
1 2 3 4 5 6
a b c d e f
6 5 4 3 2 1
f e d c b a
A B C D E F
7 8 9 10 11 12
Explanation:
using map is the premier way to iterate over lists of things where you don't need to know how many you've seen (otherwise, a for loop is needed)
the adding of " around the cell contents is only to prove they've been processed. Otherwise the second line could just be: map { say join ' ', #$_ } #lines;

Is it possible to assign two variables in Perl foreach loop?

Is it possible to assign two variables the same data from an array in a Perl foreach loop?
I am using Perl 5, I think I came across something in Perl 6.
Something like this:
my $var1;
my $var2;
foreach $var1,$var2 (#array){...}
It's not in the Perl 5 core language, but List::Util has a pairs function which should be close enough (and a number of other pair... functions which may be more convenient, depending on what you're doing inside the loop):
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
use List::Util 'pairs';
my #list = qw(a 1 b 2 c 3);
for my $pair (pairs #list) {
my ($first, $second) = #$pair;
say "$first => $second";
}
Output:
a => 1
b => 2
c => 3
The easiest way to use this is with a while loop that calls splice on the first two elements of the array each time,
while (my($var1, $var2) = splice(#array, 0, 2)) {
...
}
However, unlike foreach, this continually does a double-shift on the original array, so when you’re done, the array is empty. Also, the variables assigned are copies, not aliases as with foreach.
If you don’t like that, you can use a C-style for loop:
for (my $i = 0; $i < #array; $i += 2) {
my($var1, $var2) = #array[$i, $i+1];
...
}
That leaves the array in place but does not allow you to update it the way foreach does. To do that, you need to address the array directly.
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #pairlist; $i += 2) {
$pairlist[ $i + 0 ] x= 2;
$pairlist[ $i + 1 ] *= 2;
}
print "Array is #pairlist\n";
That prints out:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
You can get those into aliased variables if you try hard enough, but it’s probably not worth it:
my #kvlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for (my $i = 0; $i < #kvlist; $i += 2) {
our ($key, $value);
local(*key, $value) = \#kvlist[ $i, $i + 1 ];
$key x= 2;
$value *= 2;
}
print "Array is #kvlist\n";
Which prints out the expected changed array:
Array is feefee 2 fiefie 4 foefoe 6 fumfum 8
Note that the pairs offered by the List::Pairwise module, which were but very recently added to the core List::Util module (and so you probably cannot use it), are still not giving you aliases:
use List::Util 1.29 qw(pairs);
my #pairlist = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
for my $pref (pairs(#pairlist)) {
$pref->[0] x= 2;
$pref->[1] *= 2;
}
print "Array is #pairlist\n";
That prints out only:
Array is fee 1 fie 2 foe 3 fum 4
So it didn’t change the array at all. Oops. :(
Of course, if this were a real hash, you could double the values trivially:
for my $value (values %hash) { $value *= 2 }
The reasons that works is because those are aliases into the actual hash values.
You cannot change the keys, since they’re immutable. However, you can make a new hash that’s an updated copy of the old one easily enough:
my %old_hash = (
fee => 1,
fie => 2,
foe => 3,
fum => 4,
);
my %new_hash;
#new_hash{ map { $_ x 2 } keys %old_hash } =
map { $_ * 2 } values %old_hash;
print "Old hash is: ", join(" " => %old_hash), "\n";
print "New hash is: ", join(" " => %new_hash), "\n";
That outputs
Old hash is: foe 3 fee 1 fum 4 fie 2
New hash is: foefoe 6 fiefie 4 fumfum 8 feefee 2
A general algorithm for more than 2 variables:
while( #array ){
my $var1 = shift #array;
my $var2 = shift #array;
my $var3 = shift #array;
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PS: Using a working copy of the array to that it is preserved for use later:
if( my #working_copy = #array ){
while( #working_copy ){
my $var1 = shift #working_copy;
my $var2 = shift #working_copy;
my $var3 = shift #working_copy;
# other variables from #working_copy
# do things with $var1, $var2, $var3, ...
}
}
PPS: another way is to use indexing. Of course, that is a sure sign that the data structure is wrong. It should be an array of arrays (AoA) or an array of hashes (AoH). See perldoc perldsc and perldoc perllol.
my $i = 0;
while( $i < #array ){
my $var1 = $array[ $i++ ];
my $var2 = $array[ $i++ ];
my $var3 = $array[ $i++ ];
# other variables from #array
# do things with $var1, $var2, $var3, ...
}
PPPS: I've been asked to clarify why the data structure is wrong. It is a flatten set of tuples (aka records aka datasets). The tuples are recreated by counting of the number of data for each. But what is the reader constructing the set has a bug and doesn't always get the number right? If, for a missing value, it just skips adding anything? Then all the remaining tuples are shifted by one, causing the following tuples to be grouped incorrectly and therefore, invalid. That is why an AoA is better; only the tuple with the missing data would be invalid.
But an better structure would be an AoH. Each datum would access by a key. Then new or optional data can be added without breaking the code downstream.
While I'm at it, I'll add some code examples:
# example code for AoA
for my $tuple ( #aoa ){
my $var1 = $tuple->[0];
my $var2 = $tuple->[1];
my $var3 = $tuple->[2];
# etc
}
# example code for AoH
for my $tuple ( #aoh ){
my $var1 = $tuple->{keyname1};
my $var2 = $tuple->{key_name_2};
my $var3 = $tuple->{'key name with spaces'};
my $var4 = $tuple->{$key_name_in_scalar_variable};
# etc
}
Here is a module-less way to "loop" by an arbitrary value ($by) and output the resulting group of elements using an array slice:
#!perl -l
#array = "1".."6";
$by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
print "#array[$i..$i+$by]";
$i++ ;
}
As a one-liner to test (cut and paste to a Unix shell):
perl -E '#array = "1".."6"; $by = 3; $by--;
for (my $i = 0 ; $i < #array ; $i += $by ) {
say "#array[$i..$i+$by]"; $i++ }'
Output:
1 2 3
4 5 6
If you make $by = 2; it will print pairs of numbers. To get at specific elements of the resulting slice access it as an anonymous array: (e.g. [#array[$i..$i+$by]]->[1]).
See also:
How do I read two items at a time in a Perl foreach loop?
Perl way of iterating over 2 arrays in parallel
Some good responses there, including reference to natatime which is quite easy to use. It's easy to implement too - it is essentially a wrapper around the splice solutions mentioned in the responses here.
The following is not the nicest example, but I've been using autobox::Core and made an #array->natatime() "method" ;-) like this:
use autobox::Core ;
sub autobox::Core::ARRAY::natatime {
my ($self, $by) = #_;
my #copy = #$self ;
my #array ;
push #array, [splice (#copy, 0, $by) ] while #copy ;
if ( not defined wantarray ) {
print "#{ $_ } \n" for #array ;
}
return wantarray ? #array : \#array;
}
The #copy array is spliced destructively, but $self (which is how the #array in front of the autobox method -> arrow gets passed to the function) is still there. So I can do:
my #dozen = "1" .. "12" ; # cakes to eat
#dozen->natatime(4) ; # eat 4 at time
my $arr_ref = #dozen->natatime(4) ; # make a reference
say "Group 3: #{ $arr_ref->[2] }" ; # prints a group of elements
say scalar #dozen , " cakes left" ; # eat cake; still have it
Output:
1 2 3 4
5 6 7 8
9 10 11 12
Group 3: 9 10 11 12
12 cakes left
One other approach that also uses a CPAN module (I gave this answer elsewhere but it is worth repeating). This can also be done non-destructively, with Eric Strom's excellent List::Gen module:
perl -MList::Gen=":all" -E '#n = "1".."6"; say "#$_" for every 2 => #n'
1 2
3 4
5 6
Each group of elements you grab is returned in an anonymous array so the individual values are in: $_->[0] $_->[1] ... etc.
You mentioned Perl6, which handles multiple looping values nicely:
my #qarr = 1 .. 6;
my ($x, $y, $z) ;
for #qarr -> $x , $y , $z { say $x/$y ; say "z = " ~ $z }
Output:
0.5
z = 3
0.8
z = 6
For more on the Perl6 approach see: Looping for Fun and Profit from the 2009 Perl6 Advent Calendar, or the Blocks and Statements Synopsis for details. Perhaps Perl 5 will have a similar "loop by multliple values" construct one day - à la perl5i's foreach :-)

searching two array string for equal words

I am a beginner in Perl. I have two string arrays array1 and array2. I want to check the each and every element in 2nd array. if there is i want to give a relative value one to that particular element in the 2nd array. the relative values are store in an array.I try it out but it wont work and git gives a warning like" Use of uninitialized value in string eq at pjt.pl line 52, line 3".
while($i <= (scalar #resultarray-1))
{
while ($j <= (scalar #inputsymbl-1))
{
if ($resultarray[$i] eq $inputsymbl[$j])
{
$rel[$j]=1;
$i=$i+1;
$j=0;
}
else
{
$j=$j+1;
}
}
if($j==(scalar #inputsymbl))
{
$i=$i+1;
$j=0;
}
}
try this:
my $i = 0;
my $j = 0;
## walk each array element
foreach(#resultarray) {
my $result = $_;
foreach(#inputsymbl) {
my $symbl = $_;
if ($result eq $symbl) {
$rel[$j] = 1;
$i++;
} else {
$j++;
}
}
if ($j == (scalar #inputsymbl - 1)) {
$i++;
$j = 0;
}
}
provide more informations if you need detailed help.
From your question and code, it appears that you want to flag the indexes, by using a third array, of the two array's elements that are equal. By doing this, however, you're creating a sparse array. Also, if the two arrays don't have the same number of elements, a "Use of uninitialized value in string eq..." warning will eventually occur. Given these issues, consider using the smaller index of the two arrays (done using the ternary operator below) and pushing the indexes of the equal elements onto the third array:
use strict;
use warnings;
use Data::Dumper;
my #results;
my #arr1 = qw/A B C D E F G H I J/;
my #arr2 = qw/A D C H E K L H N J P Q R S T/;
# Equal: ^ ^ ^ ^ ^
# Index: 0 2 4 7 9
for my $i ( 0 .. ( $#arr1 <= $#arr2 ? $#arr1 : $#arr2 ) ) {
push #results, $i if $arr1[$i] eq $arr2[$i];
}
print Dumper \#results;
Output:
$VAR1 = [
0,
2,
4,
7,
9
];
Hope this helps!

Perl: correctly print array of arrays (dereference)

Hey fellow perl monks,
I'm still wrapping my head around how to correctly dereference. (I read the similar posts prior to posting, but unfortunately am still a bit cloudy on the concept.)
I have the following array, which internally is composed of two arrays. (BTW, I am using strict and warning pragmas.)
use strict; use warnings;
my #a1; my #a2;
where:
#a1 = ( "1MB", "2MB", ... )
and..
#a2 = ( "/home", "/home/debug", ... )
Both #a1 & #a2 are arrays which contain 51 rows. So, I populate these into my 2nd array.
my #b;
push (#b, [ #a1, #a2 ]);
However, when I try to print the results of #b:
sub newl { print "\n"; print "\n"; }
my $an1; my #an1;
$an1 = $#a1;
#an1 = ( 0, 1..$an1 );
for my $i (#an1) { print #b[$i]; &newl; }
I see references to the arrays:
ARRAY(0x81c0a10)
.
ARRAY(0x81c0a50)
.
.
.
How do I properly print this array? I know I need to dereference the array, I'm not sure how to go about doing this. I tried populating my array as such:
push (#b, [ \#a1, \#a2 ]);
Which produces the same results. I also tried:
for my $i (#an1) { print #{$b[$i]}; &newl; }
Which unfortunately errors due to having 0 as an array reference?
Can't use string ("0") as an ARRAY ref while "strict refs" in use at p_disk_ex6.pl line 42.
Any suggestions are greatly appreciated!
A short example program, which might help you:
use strict;
use warnings;
my #a1 = qw(1MB 2MB 10MB 7MB);
my #a2 = qw(/foo /bar /flub /blub);
my #b = (\#a1, \#a2);
# equivalent long version:
# my #b = ();
# $b[0] = \#a1;
# $b[1] = \#a2;
for (my $i = 0; $i <= $#a2; $i++) {
print "a1[$i]: $b[0][$i]\n";
print "a2[$i]: $b[1][$i]\n";
print "\n";
}
In your example you were pushin an anoymous arrayref [] into #b. Therefore $b[0] contained the arrayref.
my #b;
push (#b, [ \#a1, \#a2 ]);
# this corresponds to:
# $b[0][0] = \#a1;
# $b[0][1] = \#a2;
In the example where you wrote [#a1, #a2] you were creating an array_ref which contained the joined arrays #a1 and #a2 (first all elements of #a1, and then all elements of #a2):
my #b;
push(#b , [#a1, #a2]);
# $b[0] = ['1MB' , '2MB', '10Mb', '7MB', '/foo', '/bar', '/flub', '/blub']
Even Simply this also works
use strict;
use warnings;
my #a1 = qw(1MB 2MB 10MB 7MB);
my #a2 = qw(/foo /bar /flub /blub);
my #b = (#a1, #a2);
print "#b";
If you want a general solution that doesn't assume how many elements there are in each of the sub-arrays, and which also allows arbitrary levels of nesting, you're better off using packages that someone else has already written for displaying recursive data structures. A particularly prevalent one is YAML, which you can install if you don't already have it by running cpan:
$ cpan
Terminal does not support AddHistory.
cpan shell -- CPAN exploration and modules installation (v1.9800)
Enter 'h' for help.
cpan[1]> install YAML
Then you can display arbitrary data structures easily. To demonstrate with a simple example:
use YAML;
my #a1 = qw(1MB 2MB 10MB 7MB);
my #a2 = qw(/foo /bar /flub /blub);
my #b = (\#a1, \#a2);
print Dump(\#b);
results in the output
---
-
- 1MB
- 2MB
- 10MB
- 7MB
-
- /foo
- /bar
- /flub
- /blub
For a slightly more complicated example
my #b = (\#a1, \#a2,
{ a => 0, b => 1 } );
gives
---
-
- 1MB
- 2MB
- 10MB
- 7MB
-
- /foo
- /bar
- /flub
- /blub
- a: 0
b: 1
To read this, the three "-" characters in column 1 indicate an array with three elements.
The first two elements have four sub elements each (the lines with "-" in column 3). The
third outer element is a hash reference, since it is made up of "key: value" pairs.
A nice feature about YAML is that you can use it to dump any recursive data structure into a file, except those with subroutine references, and then read it back later using Load.
If you really have to roll your own display routine, that is certainly possible, but you'll have a much easier time if you write it recursively. You can check whether your argument is an array reference or a hash reference (or a scalar reference) by using ref:
my #a1 = qw(1MB 2MB 10MB 7MB);
my #a2 = qw(/foo /bar /flub /blub);
my #b = (\#a1, \#a2,
{ a => 0, b => 1 } );
print_recursive(\#b);
print "\n";
sub print_recursive {
my ($obj) = #_;
if (ref($obj) eq 'ARRAY') {
print "[ ";
for (my $i=0; $i < #$obj; $i++) {
print_recursive($obj->[$i]);
print ", " if $i < $#$obj;
}
print " ]";
}
elsif (ref($obj) eq 'HASH') {
print "{ ";
my #keys = sort keys %$obj;
for (my $i=0; $i < #keys; $i++) {
print "$keys[$i] => ";
print_recursive($obj->{$keys[$i]});
print ", " if $i < $#keys;
}
print " }";
}
else {
print $obj;
}
}
which produces the output
[ [ 1MB, 2MB, 10MB, 7MB ], [ /foo, /bar, /flub, /blub ], { a => 0, b => 1 } ]
I have not written my example code to worry about pretty-printing, nor does it
handle scalar, subroutine, or blessed object references, but it should give you the idea of how you can write a fairly general recursive data structure dumper.

Resources