Using an array to index into another array - arrays

I have two arrays, let's call them #a1 and #a2. What I'm trying to do is obtain elements from #a2 using the values in #a1 as indices. My current attempt doesn't work properly.
foreach (#a1) {
print $a2[$_] . "at" . $_;
}
This only prints $_ but not $a2[$_].
I sense there is a trivial solution to this, but I just can't find it.

There is nothing wrong with the code you have. I have tested a small script and it works as expected. Asi i suggested in my comment, try using something like Data::Dumper to see whats in the arrays before the loop.
use strict;
use warnings;
use Data::Dumper;
my #a1 = (0..4);
my #a2 = ("a".."e");
print Dumper \#a1, \#a2;
foreach (#a1){
print $a2[$_]." at ".$_."\n";
}
OUTPUT
$VAR1 = [
0,
1,
2,
3,
4
];
$VAR2 = [
'a',
'b',
'c',
'd',
'e'
];
a at 0
b at 1
c at 2
d at 3
e at 4

there's no reason your code shouldn't work as long as the values of the first array are valid addresses in the second array. but if all you really want to do is just get the values and address of the second array, you could just do:
for my $i (0..$#a2) {
print "$i: $a2[$i]","\n";
}
$#a2 is the last element address of the array.

Related

Perl: slicing an array of hashes

The output of the code below is always empty. Not sure what I am doing wrong and would appreciate any help. How do I get to the values of a key in a specific hash in an array of hashes?
use strict;
use warnings;
my %dot1 = ('a'=>1,'b'=>2);
my %dot2 = ('a'=>3,'b'=>4);
my %dot3 = ('a'=>5,'b'=>6);
my %dot4 = ('a'=>7,'b'=>8);
my #array = (%dot1,%dot2,%dot3,%dot4);
my %x = $array[2];
my $y = $x->{'a'};
print "$y \n";
You don't have an array of hashes. You have an array that looks like a hash, where the keys a and b will be there four times each, in relatively random order.
print Dumper \#array;
$VAR1 = [
'a',
1,
'b',
2,
'a',
3,
'b',
4,
'a',
5,
'b',
6,
'a',
7,
'b',
8
];
Afterwards, you are using $x->{a}, which is the syntax to take the key a from the hashref $x, but you only ever declared a hash %a. That in turn breaks, because you give it an odd-sized list of one value.
Instead, add references to the hashes to your array. That way you will get a multi-level data structure instead of a flat list. Then make the x variable a scalar $x.
my %dot1 = ('a'=>1,'b'=>2);
my %dot2 = ('a'=>3,'b'=>4);
my %dot3 = ('a'=>5,'b'=>6);
my %dot4 = ('a'=>7,'b'=>8);
my #array = (\%dot1,\%dot2,\%dot3,\%dot4); # here
my $x = $array[2]; # here
my $y = $x->{'a'};
print "$y \n";
This will print 5.
You should read up on data structures in perlref and perlreftut.
If you want an array of hash references, you need to say so explicitly.
my #array = (\%dot1, \%dot2, \%dot3, \%dot4);
my %x = %{$array[2]};
my $y = $x{a};
print "$y\n";
What you want to do is add references to your hashes to your #array, otherwise perl will evaluate the hashes in list context.
my #array = (\%dot1,\%dot2,\%dot3,\%dot4);
my $x = $array[2];
my $y = $x->{'a'};
print "$y \n";

How can I print a multidimensional array in Perl?

I am trying to print a multidimensional array (matrix) in Perl passing reference to array to subroutine.
Here is my code:
sub print_matrix(\#) {
my $array = shift;
for my $i ( 0 .. $#{ $array } ) {
my $row = $array[$i];
for my $j ( 0 .. $#($row) ) {
print $array[$i][$j];
}
}
}
Borodin tells you what was wrong with your code.
Now consider this module: Data::Dumper (available on CPAN). You can use this module to print any data structure: arrayref of arrayrefs (what you called a matrix), hashref of hashrefs, arrayref of hashrefs, hashref of hashrefs, or any other combination of these structures for as many dimensions as you want. Of course, if you have too many dimensions, it could lead to a confusing output.
My point is, some time ago, I was asked in an interview how I would implement this module. I thought it was a very clever question. I had to think a little because I use the module often but never bothered to figure how it works. It is in fact very simple. Imagine in your subroutine you receive a reference but you don't actually know what kind of reference it is (scalarref, arrayref, hashref, etc.), how would you determine what it is? If you have multiple possibilities, what would you do to cover all of them? Have you thought of creating a recursive function?
So, to solve your problem quickly, if you just want to print your matrix for debugging purpose, use Data::Dumper. Otherwise, if you want to do something more complex and wish to cover multiple cases, try to create a recursive function.
Here's a Data::Dumper example:
my $arrayref = [
[ qw/ a b c d / ],
[ qw/ e f g h / ],
[ qw/ i j k l / ],
];
use Data::Dumper;
print Dumper $arrayref;
And here's the result you will get:
$VAR1 = [
[
'a',
'b',
'c',
'd'
],
[
'e',
'f',
'g',
'h'
],
[
'i',
'j',
'k',
'l'
]
];
Each "row" of your matrix is printed as a list of elements, separated by a comma (and a new line), inside a pair of brackets. Be careful, if you pass it an array, it will print each elements one by one, and you will lose the "dimensions". If you only have an array, you have to pass it as a reference like this:
print Dumper \#array;
I hope this helps.
Using plain print is OK when all you have are single letter entries in your matrix, but a module like Text::Table can make it much easier to produce tidy output. For example,
#!/usr/bin/env perl
use strict;
use warnings;
use Text::Table;
my #matrix = map {
[ map sprintf('%.2f', -500 + rand(1000)), 1 .. 5 ]
} 1 .. 5;
my $mat = Text::Table->new;
$mat->load(#matrix);
print $mat;
Output:
-7.73 -83.85 -351.18 21.06 320.40
174.83 238.29 91.16 361.43 213.04
446.43 -4.82 322.81 10.38 -436.62
-128.05 195.68 199.05 288.39 115.30
-251.19 -329.35 244.13 -428.25 454.64
You can print a two-dimensional Perl array very simply with something like this
use strict;
use warnings;
my #arr_2d = (
[ qw/ a b c d / ],
[ qw/ e f g h / ],
[ qw/ i j k l / ],
);
print_2d(\#arr_2d);
sub print_2d {
my ($matrix) = #_;
print "#$_\n" for #$matrix;
}
output
a b c d
e f g h
i j k l
Update
Here's a working version of your own code. You weren't using array references properly and had parentheses where there should have been braces. This version also prints a space after each element and a newline after each row.
sub print_matrix {
my $array = shift;
for my $i ( 0 .. $#{ $array } ) {
my $row = $array->[$i];
for my $j ( 0 .. $#{ $row } ) {
print $array->[$i][$j], ' ';
}
print "\n";
}
}

Two-dimensional array access in Perl

I have an array populated with cities. I want to pass the array by reference to a sub routine and print each city to output. However, I have the following problems:
I can access each element before my while loop in the subroutine. But I cannot access the elements within my while loop. I get the error message:
...
Use of uninitialized value in print at line 44, line 997 (#1)
Use of uninitialized value in print at line 44, line 998 (#1)
...
The following is some code. I have commented what prints and what doesn't (I tried to cut out code that is not needed for my explanation...):
#cities;
# Assume cities is loaded successfully
&loadCities(getFileHandle('cities.txt'), $NUM_CITIES, \#cities);
&printElements(getFileHandle('names.txt'), \#cities);
sub printElements{
my $counter = 0;
my $arraySize = scalar $_[1];
# Prints fine!!!
print #{$_[1][($counter)%$arraySize];
while ((my $line = $_[0]->getline()) && $counter < 1000){
# Doesn't print. Generates the above error
print #{$_[1][($counter)%$arraySize];
$counter += 1;
}
}
The Perl syntax has me super confused. I do not understand what is going on with #{$_[1]}[0]. I am trying to work it out.
$_[1], treat the value at this location as scalar value (memory
address of the array)
#{...}, interpret what is stored at this
memory address as an array
#{...} [x], access the element at index x
Am I on the right track?
My first tip is that you should put use strict; and use warnings; at the top of your script. This generally reveals quite a few things.
This line: print #{$_[1][($counter)%$arraySize]; doesn't have a closing }. You also don't need the parenthesis around $counter.
Like you mentioned, the best/most clear way to get the length of an array is my $arraySize = scalar #{$_[1]};.
You can check out the documentation here for working with references. I'll give you a quick overview.
You can declare an array as normal
my #array = (1, 2, 3);
Then you can reference it using a backslash.
my $array_ref = \#array;
If you want to use the reference, use #{...}. This is just like using a regular array.
print #{$array_ref};
You could also declare it as a reference to begin with using square braces.
my $array_ref = [1, 2, 3];
print #{$array_ref}; # prints 123
In Perl, a 2-dimensional array is actually an array of array references. Here is an example:
my #array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
print #{$array[1]}; # prints def
Now let's try passing in an array reference to a subroutine.
my #array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
example(\#array); # pass in an array reference
sub example {
my #arr = #{$_[0]}; # use the array reference to assign a new array
print #{$arr[1]};
print #{$_[0][1]}; # using the array reference works too!
}
Now let's put it together and print the whole 2-d array.
my #array = ( ['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'] );
example(\#array);
sub example {
my #arr = #{$_[0]};
for my $ref (#arr) {
print #{$ref};
}
} # prints abcdefghi
You could adapt this example pretty easily if you wanted to use it for your printElements subroutine.
Another note about printing the elements in an array. Let's take this line from the last example:
print #{$ref};
Since we are calling it every time through the loop, we may want to print a new line at the end of it.
print #{$ref} . "\n";
What does this print? Try it! Does it work?
This is where the built-in subroutine join comes in handy.
print join(" ", #{$ref}) . "\n";
For loops are generally the best way to iterate through an array. My answer here talks a little about doing it with a while loop: https://stackoverflow.com/a/21950936/2534803
You can also check out this question: Best way to iterate through a Perl array
To make references a bit easier to understand, I prefer the -> syntax instead of the munge-it-all-together syntax.
Instead of:
#{$_[1]}[0].
Try
$_[1]->[0];
It means the same thing. It's easier, and it's cleaner to see. You can see that $_[1] is an array reference, and that you're referencing the first element in that array reference.
However, an even better way is to simply set variables for your various element in #_. You have to type a few more letters, but your code is much easier to understand, and is a lot easier to debug.
sub print_elements {
my $file_handle = shift; # This isn't a "reference", but an actual file handle
my $cities_array_ref = shift; # This is a reference to your array
my #cities = #{ $cities_array_ref }; # Dereferencing makes it easier to do your program
Now, your subroutine is dealing with variables which have names, and your array reference is an array which makes things cleaner. Also, you cannot accidentally affect the values in your main program. When you use #_, it's a direct link to the values you pass to it. Modifying #_ modifies the value in your main program which is probably not what you want to do.
So, going through your subroutine:
sub printElements {
my file_handle = shift;
my $cities_array_ref = shift;
my #cities = #{ $cities_array_ref };
my $counter;
my $array_size = #cities; # No need for scalar. This is automatic
while ( my $line = $file_handle->getline and $counter < 1000 ) {
chomp $line;
my $city_number = $counter % $array_size;
print $cities[$city_number]. "\n";
$counter += 1;
}
}
Note, how much easier it is to see what's going on by simply assigning a few variables instead of trying to cram everything together. I can easily see what your parameters to your subroutine are suppose to be. If you called the subroutine with the incorrect parameter order, you could easily spot it. Also notice I broke out $counter % $array_size and assigned that to a variable too. Suddenly, it's obvious what I'm trying to get out of it.
However, I can't see where you're using the $line you're getting with getline. Did I miss something?
By the way, I could have done this without referencing the array in the while loop too:
sub printElements {
my file_handle = shift;
my $cities = shift; # This is an array reference!
my $counter;
my $array_size = #{ $cities }; # I need to deref to get an array
while ( my $line = $file_handle->getline and $counter < 1000 ) {
chomp $line;
my $city_number = $counter % $array_size;
print $cities->[$city_number]. "\n"; # That's it!
$counter += 1;
}
}
See how that -> syntax makes it easy to see that $cities is a reference that points to an array? A lot cleaner and easier to understand than ${$cities}[$city_number].
This code wouldn't actually compile.
print #{$_[1][($counter)%$arraySize];
It probably wants to be
print $_[1]->[($counter)%$arraySize];
after you fix arraySize.
If the result is somehow a pointer to an array then
print "#{$_[1]->[($counter)%$arraySize]}";
I figured how to solve my #1 problem (still looking for help on my #2 if anyone can).
I changed
my $arraySize = scalar $_[1];
to
my $arraySize = #{$_[1]};
And my second print statement is printing the way I want.
It seems that scalar $_[1] was taking the memory address of the array and I was moding against this allowing my $counter to go way beyond the number of elements in the array.
References confuse me too! I always like to dereference them as soon as possible. This works for me:
sub printElements{
my $counter = 0;
my $fh = $_[0];
my #array = #{$_[1]};
my $arraySize = scalar #array;
# Prints fine!!!
print #array[($counter)%$arraySize];
while ((my $line = $fh->getline()) && $counter < 1000){
#Doesn't print. Generates the above error
print #array[($counter)%$arraySize];
$counter += 1;
}
}
I'm sure someone else could explain in the comments why they think working with the reference is a better way (please do), but under the mantra of "keep it simple", I don't like working with them. Probably because I was never a C programmer...

How can I create a variable array name in Perl?

Array #p is a multiline array, e.g. $p[1] is the second line.
This code will explain what I want:
$size=#p; # line number of array #p
for($i=0; $i<$size; $i++)
{
#p{$i}= split(/ +/,$p[$i]);
}
I want the result should be like this:
#p0 = $p[0] first line of array #p goes to array #p0;
#p1 = $p[1] second line of array #p goes to array #p1;
...
...
and so on.
But above code does not work, how can I do it?
It is a bad idea to dynamically generate variable names.
I suggest the best solution here is to convert each line in your #p array to an array of fields.
Lets suppose you have a better name for #p, say #lines. Then you can write
my #lines = map [ split ], <$fh>;
to read in all the lines from the file handle $fh and split them on whitespace. The first field of the first line is then $lines[0][0]. The third field of the first line is $lines[0][2] etc.
First, the syntax #p{$i} accesses the entry with the key $i in a hash %p, and returns it in list context. I don't think you meant that. use strict; use warnings; to get warned about undeclared variables.
You can declare variables with my, e.g. my #p; or my $size = #p;
Creating variable names on the fly is possible, but a bad practice. The good thing is that we don't need to: Perl has references. A reference to an array allows us to nest arrays, e.g.
my #AoA = (
[1, 2, 3],
["a", "b"],
);
say $AoA[0][1]; # 2
say $AoA[1][0]; # a
We can create an array reference by using brackets, e.g. [ #array ], or via the reference operator \:
my #inner_array = (1 .. 3);
my #other_inner = ("a", "b");
my #AoA = (\#inner_array, \#other_array);
But careful: the array references still point to the same array as the original names, thus
push #other_inner, "c";
also updates the entry in #AoA:
say $AoA[1][2]; # c
Translated to your problem this means that you want:
my #pn;
for (#p) {
push #pn, [ split /[ ]+/ ];
}
There are many other ways to express this, e.g.
my #pn = map [ split /[ ]+/ ], #p;
or
my #pn;
for my $i ( 0 .. $#p ) {
$pn[$i] = [ split /[ ]+/, $p[$i] ];
}
To learn more about references, read
perlreftut,
perldsc, and
perlref.

Perl: Dereferencing Array

Why does the following code not work in getting into an anonymous array?
my #d = [3,5,7];
print $(#{$d[0]}[0]);
# but print $d[0][0] works.
Script 1 (original)
Because it is invalid Perl code?
#!/usr/bin/env perl
use strict;
use warnings;
my #d = [3,5,7];
print $(#{$d[0]}[0]);
When compiled (perl -c) with Perl 5.14.1, it yields:
Array found where operator expected at xx.pl line 6, at end of line
(Missing operator before ?)
syntax error at xx.pl line 6, near "])"
xx.pl had compilation errors.
Frankly, I'm not sure why you expected it to work. I can't make head or tail of what you were trying to do.
The alternative:
print $d[0][0];
works fine because d is an array containing a single array ref. Thus $d[0] is the array (3, 5, 7) (note parentheses instead of square brackets), so $d[0][0] is the zeroth element of the array, which is the 3.
Script 2
This modification of your code prints 3 and 6:
#!/usr/bin/env perl
use strict;
use warnings;
my #d = ( [3,5,7], [4,6,8] );
print $d[0][0], "\n";
print $d[1][1], "\n";
Question
So the $ in $d[0] indicates that [3,5,7] is dereferenced to the array (3,5,7), or what does the $ do here? I thought the $ was to indicate that a scalar was getting printed out?
Roughly speaking, a reference is a scalar, but a special sort of scalar.
If you do print "$d[0]\n"; you get output something like ARRAY(0x100802eb8), indicating it is a reference to an array. The second subscript could also be written as $d[0]->[0] to indicate that there's another level of dereferencing going on. You could also write print #{$d[0]}, "\n"; to print out all the elements in the array.
Script 3
#!/usr/bin/env perl
use strict;
use warnings;
$, = ", ";
my #d = ( [3,5,7], [4,6,8] );
#print $(#{$d[0]}[0]);
print #d, "\n";
print $d[0], "\n";
print #{$d[0]}, "\n";
print #{$d[1]}, "\n";
print $d[0][0], "\n";
print $d[1][1], "\n";
print $d[0]->[0], "\n";
print $d[1]->[1], "\n";
Output
ARRAY(0x100802eb8), ARRAY(0x100826d18),
ARRAY(0x100802eb8),
3, 5, 7,
4, 6, 8,
3,
6,
3,
6,
I think you are trying for this:
${$d[0]}[0]
Though of course there's always the syntactic sugar way, too:
$d[0]->[0]
The square bracket constructor creates an anonymous array, but you are storing it in another array. This means that you are storing the three element array inside the first element of a one element array. This is why $d[0][0] will return the value 3. To do a single level array use the list constructor:
my #d = (3,5,7);
print $d[0];
If you really mean to create the array inside the outer array then you should dereference the single (scalar) value as
print ${$d[0]}[0].
For more read perldoc perlreftut.

Resources