#!/usr/bin/perl
A();
B();
sub A {
my #array = qw(value_1 value_2);
$array_ref = \#array;
}
sub B {
foreach my $i ( #{$array_ref} ) {
print "Array Value: $i \n";
}
}
Since the array is declared using the 'my' keyword, could the array reference be lost ?
Can any one brief me over this.
No, the scope of the variable expires, but not the memory address. The data will remain.
Isn't this something you could have simply tried? =) I just copy/pasted your code and tried it, and it worked fine.
For proper encapsulation, though, you really should return the array ref instead:
B(A());
# Or
my $aref = A();
B($aref);
sub A {
my #array = qw(value_1 value_2);
return \#array;
}
sub B {
my $array_ref = shift;
foreach my $i ( #$array_ref ) {
print "Array Value: $i \n";
}
}
I definitely recommend using
use strict;
in Perl scripts (put it on the very beginning). In this very case it will complain about $array_ref being undeclared global. And it is likely the main source of the confusion: you use $array_ref without declaring it in any way, so it is treated as a global variable.
The array content itself is kept because it is referenced by this very variable so reference count remains greater than 0 (perl uses reference counting internally to keep track of when to remove variables).
Of course approach like shown in TLP posts (without globals) is to be recommended.
Actually there is a very good reason to use my in this example.
You actually want the variable to be re-created every time through the subroutine, otherwise you would change the values that you got earlier.
use strict;
use warnings;
use 5.10.1;
my #array;
sub A{
push #array, scalar #array; # add the length of #array to the end
return \#array;
}
my #list;
for( 'A'..'D' ){
my $current = A();
push #list, $current;
say join ' ', #$current;
}
say '';
say join ' ', #$_ for #list;
0
0 1
0 1 2
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
Notice how every copy of #array is identical.
This is why you need a new copy for every time the subroutine is called.
use strict;
use warnings;
use 5.10.1;
sub A{
state $iter = 0;
my #array;
push #array, 0..$iter++;
return \#array;
}
my #list;
for( 'A'..'D' ){
my $current = A();
push #list, $current;
say join ' ', #$current;
}
say '';
say join ' ', #$_ for #list;
0
0 1
0 1 2
0 1 2 3
0
0 1
0 1 2
0 1 2 3
Related
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.
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;
This question is related to the last point of Item 46 in Effective Perl Programming.
I tested out this function, which allows you to pass array references, but access them as local arrays:
use strict;
sub max_v_local {
local ( *a, *b ) = #_;
my $n = #a > #b ? #a : #b;
my #result;
for ( my $i = 0 ; $i < $n ; $i++ ) {
push #result, $a[$i] > $b[$i] ? $a[$i] : $b[$i];
}
#result;
}
But I got the following errors unless I don't use strict:
Variable "#a" is not imported
Variable "#b" is not imported
Global symbol "#a" requires explicit package name
Global symbol "#b" requires explicit package name
Is there a way to do this with strict?
Update
Some further background. The above subroutine was a refinement of what follows. The subroutine takes 2 arrayrefs, but using the arrayrefs in the subroutine can get messy. The above code will probably be faster and is more readable since it enables you to access the arrayrefs as local arrays.
sub max_v {
my ( $a, $b ) = #_;
my $n = #$a > #$b ? #$a : #$b; # no. of items
my #result;
for ( my $i = 0 ; $i < $n ; $i++ ) {
push #result, $$a[$i] > $$b[$i] ? $$a[$i] : $$b[$i];
}
#result;
}
I haven't paid much attention to globs prior to this, so I'm having a look at them now. Turns out that they're not as complicated as I thought.
Yes. Declare them with our:
use strict;
sub max_v_local {
local ( *x, *y ) = #_;
our (#x, #y);
my $n = #x > #y ? #x : #y;
my #result;
for ( my $i = 0 ; $i < $n ; $i++ ) {
push #result, $x[$i] > $y[$i] ? $x[$i] : $y[$i];
}
#result;
}
(It's generally not a good idea to use variables named a or b for anything except sort.)
As cjm mentions, you need to declare the variables with our (or use fully qualified names).
Now for a few tips. First off, assigning from #_ into a glob unchecked is a bit of a gamble. I would write the line like this:
our (#a, #b);
local (*a, *b) = map \#$_ => #_;
That way, you have ensured that the only things passed to your subroutine are actually array references. Perl will throw an error if the value is not an array reference. You can of course write a verbose check if you want a more detailed message:
ref eq 'ARRAY' or die "..." for #_;
our (#a, #b);
local (*a, *b) = #_;
You want to use the package variables #a and #b, so you want to use our. It's almost a per-variable no strict "vars";, and it's lexically scoped.
sub max_v_local {
local ( *a, *b ) = #_;
our ( #a, #b );
...
}
But there is a problem in your algorithm. You're comparing elements of the longer array to elements that don't exist. That will give warnings and give the wrong result for negative values. Fix:
sub max_v_local {
local ( *a, *b ) = #_;
our ( #a, #b );
my $n = #a < #b ? #a : #b;
return
( map { $a[$_] > $b[$_] ? $a[$_] : $b[$_] } 0..$n-1 ),
#a[ #b .. $#a ],
#b[ #a .. $#b ];
}
Also, it's odd to take references and return a list, though. You might want to return an array reference. (return [ ... ];)
Think it might be because you haven't explicitly declared your variable #a and #b
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.
If I have the following array in Perl:
#x = qw(a b c);
and I iterate over it with foreach, then $_ will refer to the current element in the array:
foreach (#x) {
print;
}
will print:
abc
Is there a similar way to get the index of the current element, without manually updating a counter? Something such as:
foreach (#x) {
print $index;
}
where $index is updated like $_ to yield the output:
012
Like codehead said, you'd have to iterate over the array indices instead of its elements. I prefer this variant over the C-style for loop:
for my $i (0 .. $#x) {
print "$i: $x[$i]\n";
}
In Perl prior to 5.10, you can say
#!/usr/bin/perl
use strict;
use warnings;
my #a = qw/a b c d e/;
my $index;
for my $elem (#a) {
print "At index ", $index++, ", I saw $elem\n";
}
#or
for my $index (0 .. $#a) {
print "At index $index I saw $a[$index]\n";
}
In Perl 5.10, you use state to declare a variable that never gets reinitialized (unlike ones created with my). This lets you keep the $index variable in a smaller scope, but it can lead to bugs (if you enter the loop a second time it will still have the last value):
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
my #a = qw/a b c d e/;
for my $elem (#a) {
state $index;
say "At index ", $index++, ", I saw $elem";
}
In Perl 5.12 you can say
#!/usr/bin/perl
use 5.012; # This enables strict
use warnings;
my #a = qw/a b c d e/;
while (my ($index, $elem) = each #a) {
say "At index $index I saw $elem";
}
But be warned: you there are restrictions to what you are allowed to do with #a while iterating over it with each.
It won't help you now, but in Perl 6 you will be able to say
#!/usr/bin/perl6
my #a = <a b c d e>;
for #a Z 0 .. Inf -> $elem, $index {
say "at index $index, I saw $elem"
}
The Z operator zips the two lists together (i.e. it takes one element from the first list, then one element from the second, then one element from the first, and so on). The second list is a lazy list that contains every integer from 0 to infinity (at least theoretically). The -> $elem, $index says that we are taking two values at a time from the result of the zip. The rest should look normal to you (unless you are not familiar with the say function from 5.10 yet).
perldoc perlvar does not seem to suggest any such variable.
It can be done with a while loop (foreach doesn't support this):
my #arr = (1111, 2222, 3333);
while (my ($index, $element) = each(#arr))
{
# You may need to "use feature 'say';"
say "Index: $index, Element: $element";
}
Output:
Index: 0, Element: 1111
Index: 1, Element: 2222
Index: 2, Element: 3333
Perl version: 5.14.4
Not with foreach.
If you definitely need the element cardinality in the array, use a 'for' iterator:
for ($i=0; $i<#x; ++$i) {
print "Element at index $i is " , $x[$i] , "\n";
}
No, you must make your own counter. Yet another example:
my $index;
foreach (#x) {
print $index++;
}
when used for indexing
my $index;
foreach (#x) {
print $x[$index]+$y[$index];
$index++;
}
And of course you can use local $index; instead my $index; and so and so.
autobox::Core provides, among many more things, a handy for method:
use autobox::Core;
['a'..'z']->for( sub{
my ($index, $value) = #_;
say "$index => $value";
});
Alternatively, have a look at an iterator module, for example: Array::Iterator
use Array::Iterator;
my $iter = Array::Iterator->new( ['a'..'z'] );
while ($iter->hasNext) {
$iter->getNext;
say $iter->currentIndex . ' => ' . $iter->current;
}
Also see:
each to their own (autobox)
perl5i
Yes. I have checked so many books and other blogs... The conclusion is, there isn't any system variable for the loop counter. We have to make our own counter. Correct me if I'm wrong.
Oh yes, you can! (sort of, but you shouldn't). each(#array) in a scalar context gives you the current index of the array.
#a = (a..z);
for (#a) {
print each(#a) . "\t" . $_ . "\n";
}
Here each(#a) is in a scalar context and returns only the index, not the value at that index. Since we're in a for loop, we have the value in $_ already. The same mechanism is often used in a while-each loop. Same problem.
The problem comes if you do for(#a) again. The index isn't back to 0 like you'd expect; it's undef followed by 0,1,2... one count off. The perldoc of each() says to avoid this issue. Use a for loop to track the index.
each
Basically:
for(my $i=0; $i<=$#a; $i++) {
print "The Element at $i is $a[$i]\n";
}
I'm a fan of the alternate method:
my $index=0;
for (#a) {
print "The Element at $index is $a[$index]\n";
$index++;
}
Please consider:
print "Element at index $_ is $x[$_]\n" for keys #x;
Well, there is this way:
use List::Rubyish;
$list = List::Rubyish->new( [ qw<a b c> ] );
$list->each_index( sub { say "\$_=$_" } );
See List::Rubyish.
You shouldn't need to know the index in most circumstances. You can do this:
my #arr = (1, 2, 3);
foreach (#arr) {
$_++;
}
print join(", ", #arr);
In this case, the output would be 2, 3, 4 as foreach sets an alias to the actual element, not just a copy.
I have tried like....
#array = qw /tomato banana papaya potato/; # Example array
my $count; # Local variable initial value will be 0.
print "\nBefore For loop value of counter is $count"; # Just printing value before entering the loop.
for (#array) { print "\n",$count++," $_" ; } # String and variable seperated by comma to
# execute the value and print.
undef $count; # Undefining so that later parts again it will
# be reset to 0.
print "\nAfter for loop value of counter is $count"; # Checking the counter value after for loop.
In short...
#array = qw /a b c d/;
my $count;
for (#array) { print "\n",$count++," $_"; }
undef $count;