Perl programming: continue block - perl

I have just started learning Perl scripting language and have a question.
In Perl, what is the logical reason for having continue block work with while and do while loops, but not with for loop?

From http://perldoc.perl.org/functions/continue.html
If
there is a continue BLOCK attached to
a BLOCK (typically in a while or
foreach ), it is always executed just
before the conditional is about to be
evaluated again, just like the third
part of a for loop in C.
Meaning that in the for loop, the third argument IS the continue expression, e.g. for (initialization; condition; continue), so therefore it is not needed. On the other hand, if you use for in the foreach style, such as:
for (0 .. 10) {
print "$i\n";
} continue { $i++ }
It will be acceptable.

I suspect that the continue block isn't used in for loops since it is exactly equivalent to the for loop's 3rd expression (increment/decrement, etc.)
eg. the following blocks of code are mostly equivalent:
for ($i = 0; $i < 10; $i++)
{
}
$i = 0;
while ($i < 10)
{
}
continue
{
$i++;
}

You can use a continue block everywhere it makes sense: with while, until and foreach loops, as well as 'basic' blocks -- blocks that aren't part of another statement. Note that you can use the keyword for instead of foreach for the list iteration construct, and of course you can have a continue block in that case.
As everybody else said, for (;;) loops already have a continue part -- which one would you want to execute first?
continue blocks also don't work with do { ... } while ... because syntactically that's a very different thing (do is a builtin function taking a BLOCK as its argument, and the while part is a statement modifier). I suppose you could use the double curly construct with them (basic block inside argument block), if you really had to:
do {
{
...;
continue if $bad;
...;
}
continue {
...; # clean up
}
} while $more;

Related

Perl: LABEL defined inside the foreach loop, and issue coming on jumping to the LABEL using "goto LABEL"

use strict;
use warnings;
my #content1=("sunTerio", "builds123", "build123_sunTerio", "glorai", "2131flow" , "build434_glorai");
my $tryVar;
&tty;
sub tty {
foreach my $we (7..9) {
$tryVar=1;
LABEL34: #### Line 12
foreach my $i (#content1) {
if (grep ((/sunTerio/i || /glorai/i), $i)) { #### Line 14
print "-----$i-----\n";
}
}
if ($tryVar) {
foreach my $r (1..5) {
if ($r == 2 ) {
$tryVar=0;
goto LABEL34;
}
}
}
}
}
Above is the code snippet. Here, when I am using "LABEL34" at the location where it is present (Line 12), then it is causing no issues. But when I am using "LABEL34" at the "Line 14", then it is causing issue with the (warning + error) message:
Use of "goto" to jump into a construct is deprecated at pl.pl line.
Can't "goto" into the middle of a foreach loop at pl.pl line 14.
Why is it so that when I am using LABEL34 at line 14, then only issue is coming; and not when the LABEL34 is at current position (Line 12), although both scenarios are under the foreach loop?
Thanks!
The foreach loop in question is this one:
foreach my $i (#content1) {
...
}
Jumping from outside of the foreach loop into its body makes no sense.
It makes no sense logically. A foreach loop body is executed for each item of a list. This loop body would be executed without an item.
It makes no sense practically. It would skip the evaluation of #content1 and the initialization of $i. Using $i would be undefined behaviour. And when you reach the end of the loop, what then? How can you check if you've reached the end of a list you never created in the first place?
It should ever have been allowed, and it was surely not intentional.
I can't suggest an alternative to placing the label on line 14 because you haven't said what you are trying to do. (I would normally be able to guess, but that simply makes no sense.)

C-style loops vs Perl loops (in Perl)

I feel like there is something I don't get about perl's looping mechanism.
It was my understanding that
for my $j (0 .. $#arr){...}
was functionally equivalent to:
for(my $i=0; $i<=$#arr; $i++){..}
However, in my code there seems to be some slight differences in the way they operate. specifically, the time in which they decide when to terminate. for example:
assume #arr is initialized with one variable in it.
These two blocks should do the same thing right?
for my $i (0 .. $#arr)
{
if(some condition that happens to be true)
{
push(#arr, $value);
}
}
and
for (my $i=0; $i<=$#arr; $i++)
{
if(some condition that happens to be true)
{
push(#arr, $value);
}
}
In execution however, even though a new value gets pushed in both cases, the first loop will stop after only one iteration.
Is this supposed to happen? if so why?
EDIT: Thank you for all of your answers, I am aware I can accomplish the same thing with other looping mechanisms. when I asked if there was a another syntax, I was specifically talking about using for. Obviously there isn't. as the syntax to do what I want is already achieved with the c style. I was only asking because I was told to avoid the c style but i still like my for loops.
$i<=$#arr is evaluated before each loop while (0 .. $#arr) is evaluated once before any loop.
As such, the first code doesn't "see" the changes to the #arr size.
Is there another syntax I can use that would force the evaluation after each iteration? (besides using c-style)
for (my $i=0; $i<=$#arr; $i++) {
...
}
is just another way of writing
my $i=0;
while ($i<=$#arr) {
...
} continue {
$i++;
}
(Except the scope of $i is slightly different.)
An alternative would be the do-while construct, although it is a little ungainly.
my $i;
do {
push #arr, $value if condition;
} while ( $i++ < #arr );

Perl explicit package in nested loops

I declared this array:
my #array
And in this code block...
for (my $i=0; $i<$j; $i++) {
do {
// stdout operations
} while (! ($arr != 1 ));
}
The error is specifically in the } while (! ($arr != 1 )); line.
Here's the specific error:
Global symbol "$arr" requires explicit package name at exer4bernal.pl line 71.
Why do I have this problem in 2 levels of nested loops? I never had this in only 1 level. What should I change to fix this? Thanks!
What you are seeing is totally unrelated to nesting of loop. What Perl is trying to tell you is that it doesn't know about the $arr variable. Did you mean #array or $#array?
Normally, you shouldn't be using do...while blocks. What is $arr? Where is that value declared? Where is it changed in your while loop? What is $j?
Actually, what are you trying to do with a double loop? This is usually considered bad programming because when you move from processing x elements to y elements, you increase your processing time by y2- (x * y);
Maybe this is more what you mean?
for my $index ( (0..$#array) ) {
next if $array[$index] = 1;
...
}
Note I got rid of that ugly C Style for loop and replaced it with one that's easier to understand.

Is for(1) always the same as do in perl?

for(1){
print 1;
}
do {
print 1;
}
Is it true?
Or is there any special case these two doesn't equal?
One difference is that for(1) sets $_ to the value of 1, as well:
for(1){
print $_; # prints 1
}
Also, do returns the value of the last command in the sequence:
my $x = do { 1 }; # $x = 1
my $y = for(1){ 1 }; # invalid
You might really be looking for just plain curlies.
{
print 1;
}
It has the following benefits:
Creates a lexical scope (like for (1) and do {}).
You can use next, last and redo in them (like for (1)).
It doesn't mask $_ (like do {}).
But
It can only used where a statement is expected (like for (1), but unlike do {}).
Therefore, { ... } makes more sense than for (1) { ... }, and do { ... } is useful when you want to return a value.
About the same.
You can next, last and redo a for loop, but a do is not a loop--including as part of a do-while "loop". So in a non-trivial block, you couldn't be sure. However, this will work:
do {{
...
}};
Also do will not automatically set $_ to each member of the list, the way a bare for loop will.
No. They have different compilation properties and have different effects. They are similar in only one dimension, that being that the code they introduce will not be looped over -- something they have in common with other constructs, including bare blocks and (sub {...})->().
Here's an obvious difference: for (LIST) BLOCK is a loop, whereas do BLOCK is an expression. This means that
for (1) {
say "Blurgh"
} unless 1;
doesn't compile, whereas
do {
say "Blurgh"
} unless 1;
does.

How can I call a post-statement conditional on multiple statements?

$df{key} =10 ; return ; if $result == 10 ;
gives me an error. How can I achieve this?
The post-statement form of if only works with single statements. You will have to enclose multiple statements in a block after the if condition, which itself needs to be enclosed in parentheses:
if ( $result == 10 ) {
$df{key} = 10;
return;
}
In this case, it is possible to combine the two statements with a post-statement conditional. The idea here is to combine the two statements in one by performing a Boolean evaluation.
However, this is not a good idea in general as it may short-circuit and fail to do what you expect, like when $df{key} = 0:
$df{key} = 10 and return if $result == 10;
From perlsyn:
In Perl, a sequence of statements that defines a scope is called a block
... generally, a block is delimited by curly brackets, also known as braces. We will call this syntactic construct a BLOCK.
The following compound statements may be used to control flow:
if (EXPR) BLOCK
if (EXPR) BLOCK else BLOCK
if (EXPR) BLOCK elsif (EXPR) BLOCK ... else BLOCK
You can group the statements into a do BLOCK and use a conditional
statement modifier on that compound statement.
do { $df{key} = 10; return } if $result == 10;
Unlike the and construct posted by Zaid, this is not ambiguous. You
should, however, think twice before using a conditional statement
modifier. Especially mixing if/unless statements with
if/unless statement modifiers reduces readability of your code.
The main case where in my opinion the statement modifiers make sense
are uncomplicated error paths, i.e.:
croak "foo not specified" unless exists $args{foo};
The comma operator allows one to chain together multiple statements into an expression, after which you can include the conditional:
$df{key} = 10, return if $result == 10;
I use this construct quite often when checking for error conditions:
for my $foo (something...)
{
warn("invalid thing"), next unless $foo =~ /pattern/;
# ...
}