How to create an anonymous array ([]) with 'empty slots'? - perl

I can create an array with 'empty slots' in it:
$ perl -wde 1
...
DB<1> $x[2] = 0
DB<2> x \#x
0 ARRAY(0x103d5768)
0 empty slot
1 empty slot
2 0
or
DB<3> $#y = 4
DB<4> x \#y
0 ARRAY(0x103d5718)
0 empty slot
1 empty slot
2 empty slot
3 empty slot
4 empty slot
Please note: this is not the same as assigning undef.
But how do I specify that for an anonymous array using [ and ]?
This will not work:
DB<5> x [,,0]
syntax error at (eval 27)[/usr/local/lib/perl5/5.10.0/perl5db.pl:638] line 2, near "[,"
And this fails too, since I only get the assigned value:
DB<6> x []->[2] = 0
0 0
Bonus question: how can I check for an 'empty array slot' in my Perl script?
Background: In my test scripts I would like to be able to compare array contents precisely. For example I want to distinguish between 'not assigned' and 'assigned with an undef value'.
Thanks for any insights.

use feature qw/ say /;
use strict;
use warnings;
my $aref;
$#{$aref} = 4;
$aref->[2] = undef;
$aref->[3] = '';
foreach my $idx ( 0 .. $#{$aref} ) {
say "Testing $idx.";
say "\t$idx exists." if exists $aref->[$idx];
say "\t$idx defined." if defined $aref->[$idx];
}
OUTPUT:
Testing 0.
Testing 1.
Testing 2.
2 exists.
Testing 3.
3 exists.
3 defined.
Testing 4.
We pre-allocated five spots in the anonymous array, #{$aref}. The top index is 4. We are able to find what the top index is the same way we created it; by testing the value of $#{$aref}. We can test for existence. We know everything between 0 and 4 was created. But Perl only reports "exists" for array elements that have specifically had something assigned to them (even if it's undef). Therefore, $aref->[2] is reported to exist, but isn't defined. Just for fun, we assigned '' to $aref->[3] to see a test report defined once. But the short story is that even though the array is pre-extended, we can still test for the difference between an element being initialized with undef, and an element being undef through array pre-extension, by using 'exists'.
I can't say that's documented behavior of exists. So there's no guarantee it wouldn't change someday. But it works on 5.8, 5.10, 5.12, and 5.14.
So, looking for a simple way to find which elements were initialized, which were defined, and which were not, here's an example:
use feature qw/ say /;
use strict;
use warnings;
my $aref;
$#{$aref} = 4;
$aref->[2] = undef;
$aref->[3] = '';
my #initialized = grep { exists $aref->[$_] } 0 .. $#{$aref};
my #defined = grep { defined $aref->[$_] } 0 .. $#{$aref};
my #uninitialized = grep { not exists $aref->[$_] } 0 .. $#{$aref};
my #init_undef = grep { exists $aref->[$_] and not defined $aref->[$_] } 0 .. $#{$aref};
say "Top index is $#{$aref}.";
say "These elements are initialized: #initialized.";
say "These elements are not initialized: #uninitialized.";
say "These elements were initialized with 'undef': #init_undef.";
say "These elements are defined: #defined."

That should do:
$a=[];
$#$a=4;
Update (replying to #hexcoder): In one statement:
$#{$a=[]}=4
And in one statement that returns the array:
$a = (map(($_,$#$_=4),[]))[0]
Though, not that I recommend using that construction...

Background: In my test scripts I would like to be able to compare array contents precisely. For example I want to distinguish between 'not assigned' and 'assigned with an undef value'.
You can check if the index is past the end. Beyond that, there's not much you can do.
$x = [];
undef $x->[9999];
print scalar #$x;
prints 10000. The undef $x->[9999] is equivalent to $x->[9999] = undef; Because none of the elements 0 to 9998 exist, perl will magically assign all of the intervening elements to undef.

You can only do that kind of thing from XS code (see for example Devel::Peek). Some, but not all, of it is exposed by the *::Util packages. (I've been working on a debugging/tracing package, so I know more about this than anyone should need to....)

Related

Handling with two warnings of #ARGS

Small debug question I can't solve for some reason. Consider the following code:
use warnings;
my $flag = 0;
foreach my $i (0..scalar(#ARGV)) {
$data{$OPTION} .= $ARGV[$i]." " if($flag);
$flag = 1 if($ARGV[$i] =~ /$OPTION/);
undef $ARGV[$i] if($flag);
}
I get the following two warnings:
Use of uninitialized value within #ARGV in concatenation (.) or string at line 4
Use of uninitialized value in pattern match (m//) at line 5
I get the reason is that I undefine some value of #ARGV and then it tries to check it.
The way I do it like this is because I would like to 'cut' some of the data of #ARGV before using GetOpt module (which uses this array).
How to solve it?
Let's expand on those comments a bit.
Imagine #ARGV contains four elements. They will have the indexes 0, 1, 2 and 3 (as arrays in Perl are zero-based).
And your loop looks like this:
foreach my $i (0..scalar(#ARGV)) {
You want to visit each element in #ARGV, so you use the range operator (..) to generate a list of all those indexes. But scalar #ARGV returns the number of elements in #ARGV and that's 4. So your range is 0 .. 4. And there's no value at $ARGV[4] - so you get an "undefined value" warning (as you're trying to read past the end of an array).
A better way to do this is to use $#ARGV instead of scalar #ARGV. For every array variable in Perl (say #foo) you also get a variable (called $#foo) which contains the last index number in the array. In our case, that's 3 and your range (0 .. $#ARGV) now contains the integers 0 .. 3 and you no longer try to read past the end of the array and you don't get the "undefined value" warnings.
There's one other improvement I would suggest. Inside your loop, you only ever use $i to access an element from #ARGV. It's only used in expressions like $ARGV[$i]. In this case, it's probably better to skip the middle man and to iterate across the elements in the array, not the indexes.
I mean you can write your code like this:
foreach my $arg (#ARGV) {
$data{$OPTION} .= $arg . " " if($flag);
$flag = 1 if($arg =~ /$OPTION/);
undef $arg if($flag);
}
I think that's a little easier to follow.

How to check rows returned by fetchall_arrayref method in Perl?

I am trying to understand following piece of code, in particular what's happening in line 4, 5 and 6.
I have understood most of it , but just can't seem to understand what's being done with #$r != 1; in line 4 (does #$r represents the number of rows returned?) and similarly what's happening with #$r[0] in line 5 and #$rr[0] in line 6:
1 my $sth = $dbh->prepare(" a select statement ");
2 $sth->execute();
3 my $r = $sth->fetchall_arrayref();
4 die "Failed to select " if !defined($r) || #$r != 1;
5 my $rr = #$r[0];
6 my $rec = #$rr[0];
7 print "Rec is $rec\n";
The fetchall_arrayref() returns a reference to an array of all rows, in which each element is also a reference to an array, with that row's elements.
Then the die line checks
that the top-level reference $r is defined (that the call worked), and
that the size of that array, #$r, is exactly 1 – so, that the array contains exactly one element. This betrays the expectation that the query will return one row and since the code is prepared to die for this it may well ask for one row, by fetchrow_arrayref or fetchrow_array.
The #$r dereferences the $r arrayref, and in the scalar context imposed by != we indeed get the number of elements in the list.
The line 5 is very misleading, to say the least, even as the syntax happens to be legitimate: it extracts the first element and feeds it into a scalar, but using #$r[0] syntax which would normally imply that we are getting a list, by its sigil #. It's equivalent to #{$r}[0] and is an abuse of notation.
It should either clearly get the first element, if that's the intent
my $rr = $r->[0];
or also dereference it to get the whole array
my #row = #{ $r->[0] };
if that's wanted.
The last line that you query does the exact same, using the retrieved $rr reference. But the first element of the first arrayref (row) is easily obtained directly
my $rec = $r->[0]->[0]; # or $r->[0][0]
what replaces lines 5 and 6.
See perlreftut and perldsc.
Evaluating an array (reference) in scalar context returns the number of elements in the array. The answer to your other questions is that it's just standard dereferencing using ugly syntax and useless intermediate variables.
I believe in teaching people how to fish, though, so consider this code, which is essentially what you're working with:
use strict;
use warnings;
use Data::Dumper;
my $r = [[1, 2, 3]];
my $rr = #$r[0];
my $rec = #$rr[0];
print Dumper($r, $rr, $rec);
Output:
$VAR1 = [
[
1,
2,
3
]
];
$VAR2 = $VAR1->[0];
$VAR3 = 1;
It should be easy to see what's going on now that you can see what each variable holds, right?

what is meaning of $# in perl?

Consider the following code:
my #candidates = get_candidates($marker);
CANDIDATE:
for my $i (0..$#candidates) {
next CANDIDATE if open_region($i);
$candidates[$i] = $incumbent{ $candidates[$i]{region} };
}
What is meaning $# in line 3?
It is a value of last index on array (in your case it is last index on candidates).
Since candidates is an array, $#candidates is the largest index (number of elements - 1)
For example:
my #x = (4,5,6);
print $#x;
will print 2 since that is the largest index.
Note that if the array is empty, $#candidates will be -1
EDIT: from perldoc perlvar:
$# is also used as sigil, which, when prepended on the name of
an array, gives the index of the last element in that array.
my #array = ("a", "b", "c");
my $last_index = $#array; # $last_index is 2
for my $i (0 .. $#array) {
print "The value of index $i is $array[$i]\n";
}
This means array_size - 1. It is the same as (scalar #array) - 1.
In perl ,we have several ways to get an array size ,such as print #arr,print scalar (#arr) ,print $#arr+1 and so on.No reason ,just use it.You will get familiar with some default usage in perl during your further contact with perl .Unlike C++/java ,perl use a lot of
special expression to simplify our coding , but sometimes it always make us more confused.

In Perl, what's the difference between "if defined $count" and "if $count"?

I have the following script:
#!/usr/bin/perl
use warnings;
use strict;
my $count = 0;
my ( #first , #second , #third );
while ($count <= 7){
push ( #first , $count);
push ( #second , $count) if defined $count;
push ( #third , $count) if $count;
$count++;
}
print "first: #first\n";
print "second: #second\n";
print "third: #third\n";
This produces the following output:
first: 0 1 2 3 4 5 6 7
second: 0 1 2 3 4 5 6 7
third: 1 2 3 4 5 6 7
What's the difference between putting if defined $count vs. if $count, other than the latter method won't add the zero to the array? I've searched the perldocs but couldn't find the answer.
Truth and Falsehood in perlsyn explains what values are considered false in a boolean context:
The number 0, the strings '0' and '',
the empty list (), and undef are
all false in a boolean context. All other values are true.
undef is the value of a variable that has never been initialized (or that has been reset using the undef function). The defined function returns true if the value of the expression is not undef.
if $count is false if $count is the number 0, the string '0', the empty string, undef, or an object that has been overloaded to return one of those things when used in a boolean context. Otherwise, it's true. (The empty list can't be stored in a scalar variable.)
if defined $count is false only if $count is undef.
if you see the documentation of defined in perldoc then you will find that
Returns a Boolean value telling
whether EXPR has a value other
than the undefined value undef. If EXPR is not present, $_ is
checked.
A simple Boolean test will not
distinguish among undef, zero, the
empty string, and "0" , which are all
equally false.
that means,
push ( #second , 'undef') if defined $count;
when $count = 0, then it is defined because 0 is different from undef and defined returns true, but in this case push ( #third , 'undef') if $count; if condition fails, that's why it is not pushing 0 into the array.
The defined predicate tests to see whether the variable ($count in this case) is defined at all. In the code you've written, it always will be defined inside the loop because it's always got some value.
If you were to add:
undef $count;
push ( #first , 'undef');
push ( #second , 'undef') if defined $count;
push ( #third , 'undef') if $count;
after the loop, you would see the difference. (Note that I changed the code to add the literal 'undef' instead of $count because you'll get misleading effects from adding the actual undef value.
The if decides to run its block (or single statement) by looking at the value of the expression you give it:
if( EXPR ) { ... }
If that expression is true, it runs its block. If that expression is false, it doesn't.
That expression can be just about anything. Perl evaluates the expression, reducing it to a value that is either true or false. The if() then looks at that value.
So, removing that part of your question, you're left with "What's the difference between defined $count and $count". Well, one is the return value for defined and the other is whatever value is stored in $count.
When you want to figure out what a particular bit of code is doing, reduce it in the same logical process that perl would, one step at a time. See what each step does, and you'll often be able to answer your own questions. :)
You say that you searched the documentation, but I'm not sure where you looked. If you want to look up a built-in function, you can use perldoc's -f switch:
$ perldoc -f defined
If you want to read about Perl's syntax for things such as if, that's in perlsyn.
I have a beginner's guide to the Perl docs in Perl documentation documentation.
The way I read it.
if $count is only true when $count evaluates != 0, hence the third array has no 0 in it.
if defined $count checks to see if the $count as a scalar has been created, and as you have $count scalar, it's the same as the first one.

Array initialization in Perl

How do I initialize an array to 0?
I have tried this.
my #arr = ();
But it always throws me a warning, "Use of uninitialized value". I do not know the size of the array beforehand. I fill it dynamically. I thought the above piece of code was supposed to initialize it to 0.
How do I do this?
If I understand you, perhaps you don't need an array of zeroes; rather, you need a hash. The hash keys will be the values in the other array and the hash values will be the number of times the value exists in the other array:
use strict;
use warnings;
my #other_array = (0,0,0,1,2,2,3,3,3,4);
my %tallies;
$tallies{$_} ++ for #other_array;
print "$_ => $tallies{$_}\n" for sort {$a <=> $b} keys %tallies;
Output:
0 => 3
1 => 1
2 => 2
3 => 3
4 => 1
To answer your specific question more directly, to create an array populated with a bunch of zeroes, you can use the technique in these two examples:
my #zeroes = (0) x 5; # (0,0,0,0,0)
my #zeroes = (0) x #other_array; # A zero for each item in #other_array.
# This works because in scalar context
# an array evaluates to its size.
What do you mean by "initialize an array to zero"? Arrays don't contain "zero" -- they can contain "zero elements", which is the same as "an empty list". Or, you could have an array with one element, where that element is a zero: my #array = (0);
my #array = (); should work just fine -- it allocates a new array called #array, and then assigns it the empty list, (). Note that this is identical to simply saying my #array;, since the initial value of a new array is the empty list anyway.
Are you sure you are getting an error from this line, and not somewhere else in your code? Ensure you have use strict; use warnings; in your module or script, and check the line number of the error you get. (Posting some contextual code here might help, too.)
To produce the output in your comment to your post, this will do it:
use strict;
use warnings;
my #other_array = (0,0,0,1,2,2,3,3,3,4);
my #array;
my %uniqs;
$uniqs{$_}++ for #other_array;
foreach (keys %uniqs) { $array[$_]=$uniqs{$_} }
print "array[$_] = $array[$_]\n" for (0..$#array);
Output:
array[0] = 3
array[1] = 1
array[2] = 2
array[3] = 3
array[4] = 1
This is different than your stated algorithm of producing a parallel array with zero values, but it is a more Perly way of doing it...
If you must have a parallel array that is the same size as your first array with the elements initialized to 0, this statement will dynamically do it: #array=(0) x scalar(#other_array); but really, you don't need to do that.