Going out of loop Perl - perl

I have two arrays, I am evaluating the values of one array with other. What i have done is
#array_x= qw(1 5 3 4 6);
#array_y= qw(-3 4 2 1 3);
foreach $x (#array_x){
foreach $y (#array_y){
if ($x-$y > 0){
next;
}
print "$x\n";
}
}
Here, problem is , in array_x, its first index i.e 1-(-3)=4, it satisfies, but next 1-4=-3 is not satisfying the condition, hence it should break the loop and go for next element of array_x. Here only 5 and 6 satisfies the condition with all elements of array_y, so i should get only 5,6 in the output.

Here is your loops with labels so you can break to the outer level:
XVALUE:
foreach $x (#array_x){
YVALUE:
foreach $y (#array_y){
if ($x-$y > 0){
next XVALUE;
}
print "$x\n";
}
}

You can label each loop and exit the one you want. See perldoc last
E.g.:
LINE: while (<STDIN>) {
last LINE if /^$/; # exit when done with header
#...
}

If the intention is to just find the elements which are greater than the element in the subsequent list, the following would find it in 1 iteration of each array.
use strict;
my #array_x= qw(1 5 3 4 6);
my #array_y= qw(-3 4 2 1 3);
my $max_y = $array_y[0];
foreach my $y (#array_y) {
$max_y = $y if $y > $max_y;
}
foreach my $x (#array_x) {
print "\nX=$x" if $x > $max_y;
}
Output:
X=5
X=6

Not really sure what is your need, but is this what you want?
#!/usr/bin/perl
use Modern::Perl;
my #array_x= qw(1 5 3 4 6);
my #array_y= qw(-3 4 2 1 3);
foreach my $x(#array_x){
my $OK=1;
foreach my $y(#array_y){
next if $x > $y;
$OK=0;
last;
}
say "x=$x" if $OK;
}
output:
x=5
x=6

I think you might want to rethink your method. You want to find all values in #x which are greater than all in #y. You shouldn't loop over all #y each time, you should find the max of it, then filter on the max.
use strict;
use warnings;
use List::Util 'max';
my #x= qw(1 5 3 4 6);
my #y= qw(-3 4 2 1 3);
my $ymax = max #y;
my #x_result = grep { $_ > $ymax } #x;
Or since I am crazy about the new state keyword:
use strict;
use warnings;
use 5.10.0;
use List::Util 'max';
my #x= qw(1 5 3 4 6);
my #y= qw(-3 4 2 1 3);
my #x_result = grep { state $ymax = max #y; $_ > $ymax } #x;
Edit: on re-reading previous answers, this is the same concept as angel_007, though I think this implementation is more self-documenting/readable.

Revised answer:
#!/usr/bin/perl
use strict;
use warnings;
my #array_x= qw(1 5 3 4 6);
my #array_y= qw(-3 4 2 1 3);
LABEL: for my $x (#array_x) {
for my $y (#array_y) {
next LABEL unless $x > $y;
}
print "$x\n";
}

Related

Perl String::Approx for values that taken from MySQL DataBase

When I apply String::Approx to the values taken from the database, it doesn't check the matching words.
The program is as follows...
use String::Approx qw (amatch);
my #matches = qw();
my %matchhash = qw();
my #x=$search_keyword;
my $i=0;
my $j=0;
my $l=1;
my $qry1="SELECT * FROM AuctionCategoriesName";
my $prp1 = $dbh1->prepare($qry1);
$prp1->execute();
while(my $row1=$prp1->fetchrow_hashref())
{
$y=$row1->{'Name'};
#name="(['$y'])";
#match = grep { amatch (#x, #$_) } #name;
$cnt=$#match;
if($cnt < 1)
{
$matches[$i]=$match[0];
$i++;
}
}
Connections to db are perfect.
I want to get the approximately matched names from db amoung all the values present in the $row1->{'Name'}. The values must be kept stored in $matches[$i]. I call those values as:
foreach my $k (#matches) { print "$k <br/>" }
As Richard said this line is unnecessary :-
#name="(['$y'])";
You could write $y as is and perl does the right thing.
The grep followed by the if is duplication of the same work. amatch returns true/false in scalar context so we can use that.
You could simplify your code along these lines :-
1 use String::Approx qw (amatch);
2 use strict;
3 my #matches = qw();
4 my $search_keyword = 'Hello';
5 foreach my $y ('hello', 'world')
6 {
7 if (amatch ($search_keyword, ($y)))
8 {
9 push #matches, $y;
10 }
11 }
12
13 print #matches
The output is :-
$ perl test.pl
hello

Selecting elements from an array and putting it in another array in Perl

I have an array containing 10 numbers. I want to pick numbers in array index 0,2,4,6,8 and put them in a new array. Likewise with index 1,3,5,7,9. I am new to Perl (started a few days ago).
My program:
my #b;
#a = (1,2,3,4,5,6,7,8,9,10);
for($i=0;$i<=$#a;$i++)
{
push(#b,$a[$i+1]);
}
print "#b";
What am I doing wrong?
I suggest avoiding for loop as it's easier to make mistake somewhere in its usage, and use foreach
my #a = (1,2,3,4,5,6,7,8,9,10);
my (#even, #odd);
foreach my $i (0 .. $#a) {
if ($i % 2) { push #odd, $a[$i] } else { push #even, $a[$i] }
}
You can also use map to test array index modulo % 2, and then for #even decide to filter it by () or take value for it using $a[$_]
my #even = map { $_%2 ? () : $a[$_] } 0 .. $#a;
my #odd = map { $_%2 ? $a[$_] : () } 0 .. $#a;
Simple.
my #a = (1,2,3,4,5,6,7,8,9,10);
my (#even, #odd);
for ( #a ) {
$_ % 2 ? push #odd, $_ : push #even, $_;
}
A few things:
Use the pragmas use strict; and use warnings;. These will catch a lot of errors. If you use use strict;, you'll have to declare your variables with my (sometimes you'll use our, but 99% of the time, you'll use my)
In your for loop, you're using the default variable $_. This variable is evil for a variety of reasons. (One, it's global in scope, so something else could change this variable on your and you wouldn't know.). Declare your variables except in situations where you must use $_.
Standard is to put the { on the line with the for and while. Another is to avoid the C style for loop (and to avoid foreach which is just an alias to for)
Use spaces. It's much easier to read $i <= $#a than $i<=$a.
Here's my interpretation of your program:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say); #A nicer 'print'
my #a = qw(12 13 14 15 16 17 18 19 20);
my #even;
my #odd;
for my $element (0..$#a) {
if ( $element % 2 ) {
push #odd, $a[$element];
}
else {
push #even, $a[$element];
}
}
say '#even = ' . join ': ', #even;
say '#odd = ' . join ': ', #odd;
The output:
#even = 12: 14: 16: 18: 20
#odd = 13: 15: 17: 19
Note my for loop. I use the 0..$#a to go through each element of the array. The $# is returns the last index of the array. Note that this is easier to understand than the for($i=0;$i<=$#a;$i++) that you used. It's one of the reasons why C style for loops are discouraged.
I use the modulo operator % to parse my even/odd. Modulo is like remainder division. If the number is odd, the modulo % 2 will be a 1. Otherwise, it's zero. Modulo operations are great for anything that works on a cycle.
But let's get back to your program. Here's your original code with a few minor tweaks.
I added the use strict; and use warnings;. These catch about 99% of your programming errors.
I use use feature qw(say); because say is nicer when it comes to debugging. I can take a statement, copy it, and then throw say qq(...); around it and see what it's doing.
I added a bunch of say statements to reveal the logic of your code.
Let's watch what happens. Here's your program slightly modified:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
my #b;
my #a = (1,2,3,4,5,6,7,8,9,10);
my $i;
for($i=0; $i<=$#a; $i++) {
say "Index = $i Element = $a[$i + 1]";
say qq(push(\#b, $a[$i+1]););
push(#b,$a[$i+1]);
}
print "#b";
And here's the output:
Index = 0 Element = 2
push(#b, 2);
Index = 1 Element = 3
push(#b, 3);
Index = 2 Element = 4
push(#b, 4);
Index = 3 Element = 5
push(#b, 5);
Index = 4 Element = 6
push(#b, 6);
Index = 5 Element = 7
push(#b, 7);
Index = 6 Element = 8
push(#b, 8);
Index = 7 Element = 9
push(#b, 9);
Index = 8 Element = 10
push(#b, 10);
Use of uninitialized value in concatenation (.) or string at ./test.pl line 11.
Index = 9 Element =
Use of uninitialized value within #a in concatenation (.) or string at ./test.pl line 12.
push(#b, );
Use of uninitialized value $b[9] in join or string at ./test.pl line 15.
I can see the how each push statement is being executed, and look at that, you're pushing in each and every element. Actually, you're not because you used $a[$i+1] as what you're pushing.
Using use warnings and I can see that I am trying to push the non-existant $a[10] into your #b array.
Let's change your for loop to go to every other element
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
my #b;
my #a = qw(1 2 3 4 5 6 7 8 9 10);
my $i;
for ($i=0; $i <= $#a; $i += 2) {
push #b, $a[$i];
}
The first element is $a[0]. The next element in the loop is $a[2] because I added 2 to the index instead of just incrementing it by 1. Now, I'll go through all the even elements and skip all of the odd elements.
And the output:
1 3 5 7 9
(Note that $a[0] = 1. That's why they're all odd numbers. It's why I started at 12 in my program, so $a[0] = 12 which is the even number).
My preference would be to use the while and avoid the for(...; ...; ...) construct:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
my #b;
my #a = qw(1 2 3 4 5 6 7 8 9 10);
my $i = 0;
while ( $i < $#a ) {
push #b, $a[$i];
$i += 2;
}
Even:
for($i=0;$i<=$#a;$i+=2)
{
push(#b,$a[$i]);
}
Odd:
for($i=1;$i<=$#a;$i+=2)
{
push(#b,$a[$i]);
}
List::MoreUtils has an indexes function:
use List::MoreUtils qw{indexes} ;
use 5.10.0 ;
my #a = (1,2,3,4,5,6,7,8,9,10) ;
# index of array where even
say foreach indexes { $_ % 2 == 0 } #a ;
# index of array where odd
say foreach indexes { $_ % 2 != 0 } #a ;
I admit this may be sort of inelegant and it's possibly cheating here to use a module - especially one that is not in CORE. It would be convenient if List::MoreUtils and List::Utils were just one CORE module, but still not as elegant as some the other answers here.

$array can't print anything

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.

does the scope of array reference matter in this sample code

#!/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

How to produce range with step in Perl

In Bash, seq 5 5 20 produces 5 10 15 20.
In Perl, 1..5 produces 1 2 3 4 5; does it support step?
How do I produce a range with step in Perl?
perldoc -f map is one way:
use warnings;
use strict;
use Data::Dumper;
my #ns = map { 5 * $_ } 1 .. 4;
print Dumper(\#ns);
__END__
$VAR1 = [
5,
10,
15,
20
];
See also: perldoc perlop
The range operator in Perl doesn't support steps. You could use a for loop instead:
for (my $i = 5; $i <= 20; $i += 5) {
print "$i\n";
}
The List::Gen range function does this:
use strict;
use warnings;
use feature 'say';
use List::Gen;
my $range = range 5, 20, 5;
say for #$range; # 5
# 10
# 15
# 20
say while <$range>; # TIMT1WTDI
$range->say; # TAMT2WTDI, v.0.974
say $range->str; # TAMT3WTDI, v.0.974
my $by_fives = <5 .. 20 by 5>;
say while <$by_fives>; #TAMT4WTDI
<5 .. * by 5>->say( 4 ); #TAMT5WTDI
Not as good as toolic's answer:
use warnings;
use strict;
my #ns;
for my $n (1..4) {
push(#ns, $n*5);
}
I wrote Acme::Range::Module a while back as a gag module - hence the Acme:: namespace - but it does do what you want and has tests and is supported. Here's the example code:
use Acme::Globule qw( Range );
foreach (<10..1>) {
print "$_... ";
}
print "Lift-off!\n";
# put down that crack pipe...
sub my_keys(\%) {
my #hash = %{ $_[0] };
return #hash[ glob("0,2..$#hash") ];
}
sub my_values(\%) {
my #hash = %{ $_[0] };
return #hash[ glob("1,3..$#hash") ];
}
Here is an easy solution that utilizes map and the built-in range operator:
sub range {
my ($start, $end, $step) = #_;
$step ||= 1;
return map { $_ * $step } ($start / $step .. $end / $step);
}
Notice the key point here is the map {} block. We simply divide the end
by the given step (works for negative and positive) then map each value
to the multiple of the given step.
Like the map solution :)
Here it is used to look for files starting with even numbers in a range 116 to 648:
perl -e 'foreach (map { 2 * $_ } (116/2) .. (648/2)) { system("ls -l $_*"); } '
Perl is just wonderful for some jobs and making funny one-liners :)