perl code for creating incremental arrays in for loop - perl

I want to create arrays using for loop when it satisfy the particular condition. Such that it must create arrays say #a1 #a2 #a3 #a4 ... and so on . i have tried the method given below. But, i am not able to do so. Can you plz help
#/bin/usr/perl -w
use strict;
my $i;
my $m=0;
for ($i=0 ; $i<10 ; $i++ ) {
$a$m[$i]=$i;
}
$m++;
print #a1;
print #a2;
i have tried this way of creating arrays but it is not doing so.

Create a 2d array.
#!/bin/usr/perl -w
use strict;
use Data::Dumper qw( Dumper );
my #a;
my $n = 0;
for my $m (0..1) {
for my $i (0..9) {
$a[$m][$i] = $n++;
}
}
print(Dumper(\#a));
Why it's stupid to use a variable as a variable name

Related

How foreach loop in perl works with arrays in perl

this program should execute three times but is executing only twice.
Can anyone explain how this foreach loop will work in perl.
#!/usr/bin/perl
use strict;
use warnings;
my #arr=("sandeepr", "vijay","vikas");
for my $i( #arr)
{
print #arr;
my $b=pop(#arr);
print "\n $b";
}
perlsyn:
If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.
As confused as this makes Perl, you appear to be even more confused. What are trying to do? Print the elements in reverse order? If so, you could use
for my $ele (reverse #arr) {
print("$ele\n");
}
or
for my $i (1..#arr) {
my $ele = $arr[-$i];
print("$ele\n");
}
or
while (#arr) {
my $ele = pop(#arr);
print("$ele\n");
}

Perl: How do I get the value of a variable in a loop if it is in a1 a2 a3 format

Basically i am trying to access the predefined variable in a perl program.
the variables are in the form a1 a2 a3 format.
I want to access them in a loop. In the loop I will increment postfix scalar value
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
WHAT I EXPECT:
When I print $var in loop, I need the values 10,12 .. defined earlier.
WHAT I CAN NOT DO:
I am aware that such situation can be handled with a hash. But I do not have any control over the variable naming, hence I can not use hash or change variable format.
I appreciate your help!
If you want to avoid turning off strict, you could use eval:
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
print eval "\$a$i";
}
Update: using more readable version suggested by Сухой27 in the comments
Use an array instead of multiple similarly named variables, as this is their main use case,
use strict;
use warnings;
my #a = (10,12,14,16);
for my $i (0 .. $#a) {
my $var = $a[$i];
print $var, "\n";
}
alternatively you can use array of scalar references
use strict;
use warnings;
my ($a0,$a1,$a2,$a3) = (10,12,14,16);
my #a = \($a0,$a1,$a2,$a3);
for my $i (0 .. $#a) {
my $var = ${ $a[$i] };
print $var, "\n";
}
What you are doing here is called a symbolic reference, and it is an EXTREMELY bad idea.
Please take a look through this article: http://perl.plover.com/varvarname.html
But the long and short of it is - using a variable as a variable name - which you're doing - is dangerous and unnecessary. It causes all sorts of potential problems in your code, including bugs in completely unrelated pieces of code. This is why strict won't let you do it.
More importantly - it's completely unnecessary, because perl has the hash as a native data type.
Instead of your code, consider instead:
my %h;
( $h{0}, $h{1}, $h{2}, $h{3} ) = ( 10, 12, 14, 16 );
foreach my $key ( sort keys %h ) {
print "$key = $h{$key}\n";
}
Now, it's added a few characters to your code, but by doing so - you've created a lexically scoped namespace called %h. (I'd suggest calling it something more meaningful, personally - and definitely avoid $a and $b because they have special meanings).
But there is no danger of this namespace trampling over other parts of your code, and for bonus points - you no longer need your 'for' loop, you can simply iterate on keys instead. (So you always have the right number).
(Or as another user has suggested - just use an array)
You can get round strict's restriction on dynamic variable names like this.
#!/usr/bin/perl
use strict;
use warnings;
{
no strict 'refs';
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
}
I don't think this is a good idea, though!

perl: naming hash variables read from YAML

I'm reading some information from a YAML file
groceries.yaml
# comment
fruit:
apples: 1
oranges: 1
grapes: 1
vegetables:
potatoes: 1
onions: 1
leeks: 1
into a perl script
myscript.pl
#!/usr/bin/perl
use strict;
use warnings;
use YAML::Tiny;
# Create a YAML file
my $stuff = YAML::Tiny->new;
# Open the config
$stuff = YAML::Tiny->read( 'groceries.yaml' );
print "Fruit: ", %{($stuff->[0]->{fruit})},"\n";
print "Vegetables: ", %{($stuff->[0]->{vegetables})},"\n";
exit
This works fine, but I would like to have one hash for fruit and one for vegetables. My naive attempt was
my #keys = keys %{($stuff->[0])};
foreach my $key (#keys){
my %{ $key } = %{($stuff->[0]->{$key})},"\n";
}
but clearly this doesn't work.
I'd love to understand what I'm doing wrong, and am open to different work flows that accomplish the same idea :)
Try this :
#!/usr/bin/perl
use strict;
use warnings;
use YAML::Tiny;
use Data::Dumper;
# Create a YAML file
my $stuff = YAML::Tiny->new;
# Open the config
$stuff = YAML::Tiny->read( 'groceries.yaml' );
my %fruits = %{ $stuff->[0]->{fruit} };
my %vegetables = %{ $stuff->[0]->{vegetables} };
I don't know why you put some parentheses in your code :
%{($stuff->[0]->{$key})},"\n";
I think this is the problem.
To iterate over the HASHes,
use Data::Dumper;
# ...
foreach my $key (keys %{ $stuff->[0] }) {
print Dumper $stuff->[0]->{$key};
}
Edit2
#!/usr/bin/perl
use strict;
use warnings;
use YAML::Tiny;
use Data::Dumper;
# Create a YAML file
my $stuff = YAML::Tiny->new;
# Open the config
$stuff = YAML::Tiny->read( 'groceries.yaml' );
my %top_h;
foreach my $key (keys %{ $stuff->[0] }) {
$top_h{$key} = $stuff->[0]->{$key};
}
print Dumper \%top_h;
This solution enables you to access %fruit and %vegetables. They are declared as package global variables using our so that they will be in the symbol table, which then allows you to do use symbolic references or glob assignments. You'll also need to turn off strict refs to enable this. Also see this reference.
use strict;
use warnings;
use YAML::Tiny;
use Data::Dumper;
my $stuff = YAML::Tiny->read('groceries.yml');
my %groceries = %{$stuff->[0]};
our %fruit;
our %vegetables;
{
no strict 'refs';
#no strict 'vars'; # don't need above 'our' declarations with this
while (my ($key, $val) = each %groceries) {
%$key = %$val;
# or *$key = $val;
}
}
print Dumper \%fruit;
If you don't know the keys upfront, then you'll also need to turn off strict vars so you don't need to declare the hashes before assigning to them. But then you might get a warning when you use the hash directly.
But having said all of that, I think it would be simplest to just use %groceries.
my ( $fruit, $vegetables) = #{$stuff->[0]}{ qw<fruit vegetables> };
If you want to do this in a loop, first, I would save the first "document" to a local reference.
my $yaml = $stuff->[0];
And then in a while loop, do this:
while ( my ( $k, $v ) = each %$yaml ) {
say ucfirst( $k ) . ': ' . %$v;
}
You could also use List::Pairwise and do this:
mapp { say ucfirst( $a ) . ': ' . %$b } %{ $stuff->[0] };

Why does my Perl max() function always return the first element of the array?

I am relatively new to Perl and I do not want to use the List::Util max function to find the maximum value of a given array.
When I test the code below, it just returns the first value of the array, not the maximum.
sub max
{
my #array = shift;
my $cur = $array[0];
foreach $i (#array)
{
if($i > $cur)
{
$cur = $i;
}
else
{
$cur = $cur;
}
}
return $cur;
}
Replace
my #array = shift;
with
my #array = #_;
#_ is the array containing all function arguments. shift only grabs the first function argument and removes it from #_. Change that code and it should work correctly!
Why don't you want to use something that works?
One of the ways to solve problems like this is to debug your data structures. At each step you print the data you have to see if what you expect is actually in there. That can be as simple as:
print "array is [#array]\n";
Or for complex data structures:
use Data::Dumper;
print Dumper( \#array );
In this case, you would have seen that #array has only one element, so there it must be the maximum.
If you want to see how list assignment and subroutine arguments work, check out Learning Perl.
You can write the function as:
#!/usr/bin/perl
use strict; use warnings;
print max(#ARGV);
sub max {
my $max = shift;
$max >= $_ or $max = $_ for #_;
return $max;
}
However, it would be far more efficient to pass it a reference to the array and even more efficient to use List::Util::max.

How do I find which elements in one array aren't in another?

I am new to programming and hence I am stuck on a basic level problem.
Following is code I wrote for comparison. But the result I get does not make sense to me. I would appreciate if someone could tell me what is going wrong.
There are two arrays: #array1 , #array2 of unequal length.
I wish to compare both and list down values not present in #array1.
my %temp = map {$_,$_}#array2;
for (#array1){
next if exists $temp{$_};
open (FILE, ">>/filename") or die "$!";
print FILE "$_\n";
close(FILE);
}
See the FAQ How do I compute the difference of two arrays? How do I compute the intersection of two arrays?
Adapting the code you posted:
#!/usr/bin/perl
use strict; use warnings;
my #x = 1 .. 10;
my #y = grep { $_ % 2 } #x;
my %lookup = map { $_ => undef } #y;
for my $x ( #x ) {
next if exists $lookup{$x};
print "$x\n";
}
If you're doing this for a test, which I assume you are I would highly suggest is_deeply in the newer versions of Test::More
You'll have to update Test::More
cpanp install Test::More
or if you're on perl 5.5
cpan Test::More
Then you'll have use it
use Test::More;
tests => 1
is_deeply ( \#arr1, \#arr2, 'test failed' );
If you're not doing this for testing, but you're doing this for introspective purposes and the arrays are small, I'd suggest using XXX:
cpanp install http://search.cpan.org/CPAN/authors/id/I/IN/INGY/XXX-0.12.tar.gz
Then you'll have use it
use XXX;
YYY [ \#arr1, \#arr2 ];
That's some pretty clever code you've got there. Your code is more or less identical to what the Perl FAQ says. I might be tempted to do this, however:
my %tmp = map { $_ => 1 } #array2;
my #diff = grep { not exists $tmp{$_} } #array1;
This gets everything in #array1 that's not in #array2, but avoiding all of those out-of-style looping constructs (yay for functional programming). Though what I'd really do is this:
sub comp (\#\#) {
my %t = map { $_ => 1 } #{$_[1]};
return grep { not exists $t{$_} } #{$_[0]};
}
Then you can just do:
my #diff = comp(#array1, #array2); # get items in #array1 not in #array2
#diff = comp(#arraty2, #array1); # vice versa
Or you can go to CPAN. List::Compare::Functional::complement() does what you want, though the syntax is reversed.
Swap #array1 and #array2 in your code?
For simple values like strings or numbers, the following should work
my #result;
my $hosts = [qw(host1 host2 host3 host4 host5)];
my $stie_obj = [qw(host1 host5 host6)];
#result = map { my $a=$_; my $b=grep {/$a/} #$site_obj; $b==0 ? $a : () } #$hosts;
print Dumper (#result);
Should give :
$VAR1 = 'host2';
$VAR2 = 'host3';
$VAR3 = 'host4';