Perl: Dereferencing Array - arrays

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.

Related

Using an array to index into another array

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.

Proper way to string interpolate an array element within printf

While working on Schwartz's Learning Perl, I came across an exercise where I am supposed to accept a number of user input strings, where the first input is to be the width determining right justified output for the other strings.
In other words, inputs:
10
apple
boy
Output should be:
10
apple
boy
where output is right justified by 10.
I tried using arrays to approach the problem:
#!/usr/bin/perl
use strict;
use warnings;
my #array;
while (<>) {
chomp($_);
push #array, $_ ;
}
while (#array) {
printf ("%$array[0]s \n", shift #array);
}
But after formatting and printing '10' correctly, I get errors:
$ perl test.pl
10
apple
boy
10
Invalid conversion in printf: "%a" at test.pl line 11, <> line 3.
%apples
Argument "boy" isn't numeric in printf at test.pl line 11, <> line 3.
0oys
I tried a variety of methods to force interpolation of the array element by enclosing it in braces, but all these have resulted in errors. What's the proper way to string interpolate array elements within printf (if that's the right term)?
Here's a more Perlish way to write it that avoids having to perform an explicit shift. It's a lot more do-what-I-mean since the format control variable is not part of #array from the start:
use strict;
use warnings;
my ( $length, #array ) = <>;
chomp( $length, #array );
printf "%${length}s\n", $_ for ( $length, #array );

Emptying array inside loop while using push in PERL

I am writing a subroutine that prints an array of non redundant elements from another array.
This code is inside my subroutine.
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
Then i call my subroutine in a loop inside my main program, for the first iteration it is OK and my new table contains one occurrence of my old table.
But after that #new_table keeps elements from past iterations and the print result is false.
I tried emptying #new_table inside my subroutine like this
#new_table = ();
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
But then my #new_table becomes empty in all iterations except for the first one.
What is the problem with this and how can i fix it ?
Due to incorrect scoping, you're reusing the #new_table and %seen of previous passes. Create these just before the loop.
my #new_table;
my %seen;
foreach (#old_table) { push(#new_table, $_) unless ($seen{$_}++); }
print "#new_table" . "\n";
This can be simplified to
my %seen;
my #new_table = grep { !$seen{$_}++ } #old_table;
print "#new_table\n";
You can also use
use List::MoreUtils qw( uniq );
my #new_table = uniq(#old_table);
print "#new_table\n";
You are using use strict; use warnings;, right? If not, you should be. Always.
You can try uniq from List::MoreUtils to remove redundant elements.
my #new_table = uniq(#old_table);
To quote from perldoc
uniq LIST
distinct LIST
Returns a new list by stripping duplicate values in LIST. The order of elements in the returned list is the same as in LIST. In
scalar context, returns
the number of unique elements in LIST.
my #x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # returns 1 2 3 5 4
my $x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # returns 5

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...

In Perl, what is the difference between #array[1] and $array[1]?

I have been studying array slices and frankly do not see the difference between choosing #array[1] and $array[1]. Is there a difference?
#!/usr/bin/perl
#array = (1,3);
print "\nPrinting out full array..\#array\n";
print "#array\n";
print "\n";
print "Printing out \#array[1]\n";
print "#array[1]\n";
print "Printing out \$array[1]\n";
print "$array[1]\n";
print "\n\n";
This is a FAQ.
The two forms are likely to work the same way in many contexts (but not all, as you can see from the example in the FAQ), but $array[1] expresses the intent more clearly.
$ and # are called sigils: hear what the sigils tell you
When you see $ you are dealing with a single thing.
When you see # you have a list of things.
#array[ 1 ] is a slice, a list with selected elements from the #array list. You have put only an element in this slice, the second element of #array, but it's a list anyway.
$array[ 1 ] is the second element of the list, a single value, a scalar.
#hash{ 'one', 'two' } is another kind of slice: this time we sliced an hash ( %hash ), obtaining a list with values corresponding to keys one and two.
Where's the difference? Although the difference is thin, you should avoid single element slices when you want a single value. Remember that on the right hand side of an expression, single element slices behave like scalars, but when they are on the left hand side of an expression they become a list assignment: they give list context to the expression.
If still you aren't feeling comfortable with the difference between scalar and list context, please don't use single element slices in order to avoid unexpected results in certain situations ( for instance when using the line input operator < > ).
Check the docs and happy Perl.
You already have your answer from Keith and Marco: # means list, $ means scalar (single value). I have more to add that does not fit in a comment.
You do not notice the difference because you are using a slice of one element. However, try this:
use warnings;
use strict;
use v5.10; # to enable say
my #array = (1 .. 10);
say "#array[2 .. 4]"; # [2,3,4] == "3 4 5"
say "#array[1,4,7]"; # == "2 5 8"
Here's some pointers to your code.
use strict;
use warnings;
There really is no excuse for not using these two. There is hardly any circumstance where it is better -- or easier -- to not use them. This holds especially true when you are experimenting and trying to learn perl.
If you had been using warnings, you would have gotten this additional information:
Scalar value #array[1] better written as $array[1]
Which basically is exactly the answer you have been given here, albeit said in the perl compiler's terse language.
Also, using print the way you do it is hard on the eyes. Make use of perl's flexibility:
#!/usr/bin/perl
use strict;
use warnings;
my #array = (1 .. 10);
print "
Printing out full array..\#array
#array
Printing out \#array[1]
#array[1]
Printing out \$array[1]
$array[1]
";
As you can see, this will print out newlines in a WYSIWYG fashion.
There are two differences:
The «1» is evaluated in list context in the case of the array slice (#foo[1]), but in scalar context in the case of the array indexing ($foo[1]). This has no functional effect.
#foo[1] gives a warning.
Good question - here's some differences:
$a[0] = localtime; print #a; # this prints: Tue Nov 1 18:51:13 2011
#a[0] = localtime; print #a; # this prints the amount of seconds, e.g. 13
$a[0] = grep{}, qw(6 2 8); print #a; # this prints: 3
#a[0] = grep{}, qw(6 2 8); print #a; # this prints: 6
$a[0] = reverse ("a", "b"); print #a; # this prints: ba
#a[0] = reverse ("a", "b"); print #a; # this prints: b
$a[0] = ("a", "b"); print #a; # this prints: b
#a[0] = ("a", "b"); print #a; # this prints: a
It doesn't matter if the slice is [1]. You can create more examples by finding functions that give different results when evaluated in different contexts.
Explanation of the latter (4th) example:
The $ in $a[0] creates a scalar context. The context affects the way the comma operator works, creating different results:
"Binary "," is the comma operator. In scalar context it evaluates its left argument, throws that value away, then evaluates its right argument and returns that value."
http://perldoc.perl.org/perlop.html#Comma-Operator
Hence: b
To understand the line beginning with: #a[0] consider:
($a, $b, $c) = ("a", "b", "c", "d"); # here the "d" is discarded
print $a, $b, $c; # this prints: abc
($a, $b) = ("a", "b", "c"); # here the "c" is discarded
($a) = ("a", "b"); # here the "b" is discarded
($a[0]) = ("a", "b"); # here the "b" is discarded
It would appear that parentheses at the beginning of the line create a list context. This is pretty much what it says at: http://docstore.mik.ua/orelly/perl4/prog/ch02_07.htm
"Assignment to a list of scalars also provides a list context to the righthand side, even if there's only one element in the list"
# at the beginnning of a line also creates a list context:
#a[0] = ("a", "b") means evaluate the RHS in the same way as above i.e. discard the "b"

Resources