Which "for" loop should I use in Perl? - perl

What are the arguments for using one of the for loops over the other to count through integers?
for my $i (0..$n) { ... }
for (my $i = 0; $i <= $n; $i++) { ... }

In your case, there isn't any difference.
In other use cases, in a for-each loop you don't have control of or access to the iterator.
Using your example syntax:
for my $i (0..10)
{
print "$i";
$i++;
}
The above actually creates a foreach loop - it's the same as saying foreach my $i (0..10). $i is a value returned from the list, not the iterator. The iterator is internal and you don't have any access to it; you can not control the flow of the loop.
The output from above will print 012345678910.
This:
for ( my $i = 0; $i++ ; $i <= 10)
{
print $i;
$i++;
}
That is an actual for loop. You are controlling and outputting the iterator. It will output:
0246810
In addition:
When you do for (1..$n) you are invoking the range operator vs. doing a simple comparison at the top of the loop. Performance differences, however, would be immeasurable.

The difference between the two styles of for loops in Perl is one of both clarity and efficiency.
When you look at for my $i (0 .. $n) {...} you can instantly see the range being used without having to mentally parse a larger expression.
With for (my $i = 0; $i <= $n; $i++) {...} there is quite a bit more to look at, and more places where errors can creep in.
In addition, foreach over a range is faster than the equivalent C-style loop as shown by the following benchmark:
use Benchmark 'cmpthese';
for my $mag (map 10**$_, 1 .. 6) {
print "\n$mag:\n";
cmpthese -2 => {
loop => sub {my $x = 0; for (my $i = 0; $i <= $mag; $i++) {$x += $i}},
each => sub {my $x = 0; for my $i (0 .. $mag) {$x += $i}},
};
}
which prints:
10:
Rate loop each
loop 613877/s -- -2%
each 625568/s 2% --
100:
Rate loop each
loop 79481/s -- -24%
each 104758/s 32% --
1000:
Rate loop each
loop 8140/s -- -27%
each 11220/s 38% --
10000:
Rate loop each
loop 832/s -- -26%
each 1124/s 35% --
100000:
Rate loop each
loop 81.6/s -- -26%
each 110/s 34% --
1000000:
Rate loop each
loop 6.90/s -- -26%
each 9.27/s 34% --

With the three-part for loop, you are able to $i += 3 and such if you want from within the loop, but with the foreach loop, you are not. That is the difference.

The off-by-one error that you originally had in the C-style loop shows that the C-style loop is more error prone, and should only be used if you actually need access to the loop counter. A foreach loop makes it much more difficult to make an off-by-one error.

I believe that the two loop examples you posted behave identically. In both cases, $i is lexically scoped (since you use the my keyword). I think the 0..$n syntax is clearer, but (surprise!) not everyone would agree with that.

Related

using for loop, finding prime number between 1-100

I'd tried lots of ways to get rid of this problem but... I can't find what's the problem of this code.
I use Perl and I want to find the prime number between 1-100.
use strict;
my $i=0;
my $j=0;
my $count=0;
for ($i=0; $i<101; $i++)
{
for ($j=2; $j<$i; $j++)
{
if ($i%$j==0)
{
$count+=1;
}
}
if ($count==0)
{
print "$i\n";
}
}
There are a few things to think about as you use the Sieve of Eratosthenes. Armali already pointed out that you were reusing the value in $count because you had it in a higher scope, so it didn't reset for each number you wanted to check.
But, I reformatted your code a bit:
use v5.10;
NUM: for( my $i=1; $i < 101; $i++ ) {
DIVISOR: for( my $j=2; $j < $i; $j++ ) {
next NUM if $i%$j == 0;
}
say $i;
}
Instead of using a flag variable ($count) to figure out what to do, you can use loop controls. If you find any divisor, you know that you have found a non-prime and there's no need to continue. That is, you don't need to count divisors.
When you find one, stop and move on to the next number. To do that, I've labeled the looping constructs. That way, in the inner loop I can skip to the next iteration of the outer loop. And, usually, once I label one loop I label them all but you don't need to do that.
Once you figure that part out, you don't need to do so much work. Aside from 2, you know that all the even numbers are not prime. You don't need to check those. So, instead of being clever, I'll just break out 2 as a special case:
use v5.10;
say 2;
NUM: for( my $i = 3; $i < 101; $i += 2 ) {
DIVISOR: for( my $j=2; $j < $i; $j++ ) {
next NUM if $i%$j == 0;
}
say $i;
}
The inner loop is doing too much work too. None of the numbers that you are checking are even, so you don't need to check any even divisors (or those ending 5 once you choose 5). And, you only have to go half way, so you can stop when you get to the square root of the number.
#!perl
use v5.10;
say 2;
NUM: for( my $i = 3; $i < 101; $i += 2 ) {
my $stop_at = int sqrt $i;
DIVISOR: for( my $j=3; $j <= $stop_at; $j += 2 ) {
next NUM if $i % $j == 0;
}
say $i;
}
And, for a final flourish, I'll take the top number from the command-line arguments but default to 100. With that, the comparison in the outer loop changes to <=:
#!perl
use v5.10;
my $limit = $ARGV[0] // 100;
say 2;
NUM: for( my $i = 3; $i <= $limit; $i += 2 ) {
my $stop_at = int sqrt $i;
DIVISOR: for( my $j=3; $j <= $stop_at; $j += 2 ) {
next NUM if $i % $j == 0;
}
say $i;
}
But, ikegami notes in a comment that for my $x (0..$n-1) is more idiomatic. That doesn't easily handle step sizes larger than 1. You can do various things to multiply that number to get the candidate number, or ways to generate the list ahead of time (but that means you have the list all at once). I'll switch to a while instead, and assume that these other bits do their work properly.
The $get_number is some magic subroutine that always gives us back the next number, and the is_prime does what it does to make the determination:
while( my $n = $get_number->() ) {
say $n if is_prime($n);
}
Here's one way that might work. First, there's a nifty Perl regex trick to determine primes. It doesn't matter that I'm using that because you can change it to whatever you like because it's hidden behind is_prime. The biggest benefit here is that it's short (and a bit of a show off):
#!perl
use v5.10;
my $get_number = generate_sub( $ARGV[0] // 100 );
while( my $n = $get_number->() ) {
say $n if is_prime($n);
}
sub is_prime { ( '1' x $_[0] ) !~ /\A(11+?)\1+\z/ }
sub generate_sub {
my( $limit ) = #_;
sub {
state $queue = [ 2, 3 ];
return if $queue->[0] > $limit;
push $queue->#*, $queue->[-1] + 2;
shift $queue->#*;
}
}
The generate_sub is a bit more tricky. First, the 2 makes is a bit tricky. Second, Perl doesn't have a yield like Python or Ruby (would be nice). To get around that, I'll see a queue with the first two numbers then add the next number based on the last on (so, adding 2 to 3 gets 5, and so on). That gets around the unique interval from 2 to 3. This stops if the next number in the queue is above the one that you want.
But, that's a bit complicated and only there to handle the special case of 2. I've been playing with a different idiom lately although I'm not convinced its desirable.
The state is a way to declare a persistent lexical variable. It runs only on the first execution. We'll use a state to return the first 2 right away. Then, the next time we come around, that $rc statement doesn't run and $next has 3. From there, I get the current number (0+$next so it's not the same data), and increment $next in a list, but only return the first in that list. That's just a trick that condenses the if-else:
sub generate_sub {
my( $limit ) = #_;
sub {
state $rc = do { return 2 };
state $next = 3;
return $next <= $limit ? ( 0+$next, $next += 2 )[0] : ();
}
}
I don't recommend this for your problem, but you should consider a way to generate the list of numbers so it's not tightly coupled to the problem. That way, you can get rid of the looping constructs.
But, that's much more than you needed to know.
You initialized my $count=0; outside instead of inside the outer for loop.
Besides that, $i should start from 2 rather than 0.

Can I use the size of an array without having to place it in a variable

Currently I am doing this to read the individual contents of an array
my $size = #words;
for(my $x = 0; $x < $size, $x++)
{
print $words[$x];
}
Is there away to skip the $size assignment? A way to cast the array and have one less line?
i.e.
for(my $x = 0; $x < $(#word), $x++)
{
print $words[$x];
}
Can't seem to find the right syntax.
Thanks
Replace
for (my $i = 0; $i < $(#words), $i++) { ... $words[$i] ... }
with
for (my $i = 0; $i < #words; $i++) { ... $words[$i] ... }
Just like in your assignment, an array evaluated in scalar context produces its size.
That said, using a C-style loop is complex and wasteful.
A better solution if you need the index:
for my $i (0..$#words) { ... $words[$i] ... }
A better solution if you don't need the index:
for my $word (#words) { ... $word ... }
Yes, for has a built in array iterator and for and foreach are synonyms.
for my $word (#words) {
print $word;
}
This is the preferred way to iterate through arrays in Perl. C style 3 statement for-loops are discouraged unless necessary. They're harder to read and lead to bugs, like this one.
for(my $x = 0; $x < $size, $x++)
^
should be a ;
Better to use foreach, but to your specific question, #foo in scalar context resolves to the length of the array, and $#foo resolves to the index of the last element:
foreach my $word (#words) { ... } # preferred
for(my $i = 0; $i < #words; ++$i) { my $word = $words[$i]; ... } # ok sometimes
for(my $i = 0; $i <= $#words; ++$i) { my $word = $words[$i]; ... } # same thing
(assuming that you haven't played with $[, which you shouldn't do.)
The syntax that you are searching for is actually no syntax at all. If you use an array variable anywhere where Perl knows you should be using a scalar value (like as an operand to a comparison operator) then Perl gives you the number of elements in the array.
So, based on your example, this will work:
# Note: I've corrected a syntax error here.
# I replaced a comma with a semicolon
for (my $x = 0; $x < #words; $x++)
{
print $words[$x];
}
But there are several ways that we can improve this. Firstly, let's get rid of the ugly and potentially confusing C-style for loop and replace it with a far easier to understand foreach.
foreach my $x (0 .. #words - 1)
{
print $words[$x];
}
We can also improve on that #words - 1. Instead, we can use $#words which gives the final index in the array #words.
foreach my $x (0 .. $#words)
{
print $words[$x];
}
Finally, we don't really need the index number here as we're just using it to access each element of the array in turn. Far better to iterate over the elements of the array rather than the indexes.
foreach my $element (#words)
{
print $element;
}

Perl: iterating values in for loop

print "Input value \n";
$line = <>;
chomp $line;
#val = $line;
for ($i = 1; $i <= 10; $i++){
print -#val* $i;
}
I have a simple for loop here where the user enters a value that I store into the #val, and I want my loop to iterate from 1 to 10 and print out the value of -#val * $i. Suppose my #val = 2, then I should see the output: -2 -4 -6 - 8 ... -20. But my actual output is: -1 -2 ... -10. What went wrong?
As I wrote in my comment, I can't imagine what you were trying to achieve by copying the value of $line to array #val. There are also a number of other points that I would like to make
You must always
use strict;
use warnings 'all';
at the start of all your Perl programs. You will then have to declare all of your variables with my, and it will alert you to many simple errors that you may otherwise overlook
The C-style for loop is rarely useful in Perl. It is usually best to iterate over a simple list. In your program that would be
for my $i ( 1 .. 10 ) {
...
}
So putting all that together, your program looks like this
use strict;
use warnings 'all';
print "Input value\n";
my $val = <>;
chomp $val;
for my $i ( 1 .. 10 ) {
print -$val * $i;
}
It's also worth pointing out that, when the contents of the for loop is just a single statement like this, you can use for as a statement modifier and write just
print -$val * $_ for 1 .. 10;
The problem is that you're storing your value in an array (#val). When you use that array in a scalar context (the math in the for loop) you just get the number of elements in the array. In your case 1. Change #val to $val or just use $line directly.

Perl - finding the largest palindrome product

I started learning perl recently and I wrote this code to find the largest palindrome product that can be obtained by multiplying 2 3-digit numbers. (question here: https://projecteuler.net/problem=4 )
Here is the code:
#!/usr/bin/perl
use 5.010;
sub checkpal{
for ($k=0;$k<length($_[0]);$k++){$b = substr ($_[0], $k, 1).$b;}
if ($_[0] eq $b){1}else{0}
}
$pals = $numb1 = undef;
for ($i = 998001; $i>=10000; $i--){
if (&checkpal($i)){
for ($j = 100; $j <1000; $j++){
if ( !($i % $j) && (length $j == 3) ){$numb1 = $j; $pals = $i; last;}
}
}
if (defined $numb1){last}
}
say $numb1." ".($pals/$numb1);
My idea is quite simple. It simply goes through a loop starting from 998001 (the largest value that product of 2 3-digit number can have) and check if the value is palindrome. If it is a palindrome, it goes through another loop to check if it can be obtained by multiplying 2 three digit numbers. Now, this algorithm might not be the most efficient or the best in the world, it should at least give the result. Which it isn't.
The problem isn't in the subroutine checkpal as far as I know. But the if (&checkblock($i)) block doesn't get executed even when $i is a palindrome. And I don't know why. Sorry if it is obvious or something .. but please tell me why it isn't working?
if ( !($i % $j) and length($i/$j)==3) { .. }
instead of
if ( !($i % $j) && (length $j == 3) )
as you want to check whether $i/$j has three digits, not $j which goes anyway from 100 to 999.
As a side-note,
if (checkpal($i))
can be replaced with simple
if ($i eq reverse $i)

How to sum two lists element-wise

I want to parse a file line by line, each of which containing two integers, then sum these values in two distinct variables. My naive approach was like this:
my $i = 0;
my $j = 0;
foreach my $line (<INFILE>)
{
($i, $j) += ($line =~ /(\d+)\t(\d+)/);
}
But it yields the following warning:
Useless use of private variable in void context
hinting that resorting to the += operator triggers evaluation of the left-hand side in scalar instead of list context (please correct me if I'm wrong on this point).
Is it possible to achieve this elegantly (possibly in one line) without resorting to arrays or intermediate variables?
Related question: How can I sum arrays element-wise in Perl?
No, it's because the expression ($i, $j) += (something, 1) parses as adding 1 to $j only, leaving $i hanging in void context. Perl 5 has no hyper-operators or automatic zipping for the assignment operators such as +=. This works:
my ($i, $j) = (0, 0);
foreach my $line (<INFILE>) {
my ($this_i, $this_j) = split /\t/, $line;
$i += $this_i;
$j += $this_j;
}
You can avoid the repetion by using a compound data structure instead of named variables for the columns.
First of all, your way of adding arrays pairwise does not work (the related question you posted yourself gives some hints there).
And for the parsing part: How about just splitting the lines? If your lines are formatted accordingly (whitespaces should not be a problem).
split(/\t/, $line, 2)
If you really, really want to do it in one line, you could do something like this (though I don't think you would call it elegant):
my #a = (0, 0);
foreach my $line (<INFILE>)
{
#a = map { shift(#a)+$_ } split(/\t/, $line, 2);
}
For an input of #lines = ("11\t1\n", " 22 \t 2 \n", "33\t3"); it gave me the #a = (6, 66)
I would advise you to use the split part of my answer, but not the adding up part. There is nothing wrong in using more than one line! If it makes your intention clearer, more lines are better than one. But than again I'm hardly using perl nowadays but python instead, so my perl coding style might have a "bad" influence there...
It is quite possible to swap the pair over for each addition, meaning you're always adding to the same element in each pair. (This generalises to rotating multi-element arrays if required.)
use strict;
use warnings;
my #pair = (0, 0);
while (<DATA>) {
#pair = ($pair[1], $pair[0] + $_) for /\d+/g;
}
print "#pair\n";
__DATA__
99 42
12 15
18 14
output
129 71
Here's another option:
use Modern::Perl;
my $i = my $j = 0;
map{$i += $_->[0]; $j += $_->[1]} [split] for <DATA>;
say "$i - $j";
__DATA__
1 2
3 4
5 6
7 8
Output:
16 - 20