Is for(1) always the same as do in perl? - 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.

Related

Perl “do { … } if …” as expression

my $x = do { 3; } if 1; say $x # works
my $x = (do { 3; } if 1); say $x # syntax error
How come? If the do block is an expression, why can't it be parenthesised? If it's not, how does the first one parse?
A compound statement used for flow control (if BLOCK), as well as one with the statement modifier (used here, the postfix if), cannot appear inside parenthesis.
This restriction makes sense since such a statement may or may not return a value
if executes the statement once if and only if the condition is true.
(original emphasis)
A side note. The first example runs without warnings but it has undefined behavior, what must be avoided. From the end of the section Statement Modifiers in perlsyn
NOTE: The behaviour of a my, state, or our modified with a statement modifier conditional or loop construct (for example, my $x if ...) is undefined. The value of the my variable may be undef, any previously assigned value, or possibly anything else. Don't rely on it. Future versions of perl might do something different from the version of perl you try it out on. Here be dragons.
(original emphasis)
Any instances of this should be rewritten, and Perl::Critic has a policy for it, making it easier to find them
It's not that the do that's the problem, it's the postfix if. That postfix can't appear inside the parens:
$ perl -E 'my $x = ( 1 if 1); say $x'
syntax error at -e line 1, near "1 if"
Execution of -e aborted due to compilation errors.
Instead, you can use the conditional operator ?: with a do in one of the branches:
$ perl -E 'my $x = ( time % 2 ? do { 1 } : () ); say $x'
my $x = do { 3; } if 1;
is actually equivalent to
( my $x = do { 3; } ) if 1;
Note that you shouldn't execute my conditionally. (More precisely, you shouldn't use a my variable that hasn't been executed. Your code is technically ok since the my is always executed before $x is used.)
An expression (my $x = do { ... }) modified by a statement modifier (if 1) is a statement.
The inside of parens must be an expression, not a statement.
You can't do
( $x = 3; )
( sub f { } )
( if (f()) { g() } )
( g() if f(); )
( g() if f() )
You get the idea.
if 1 is a statement modifier. my $x = do { 3; } is a statement; do { 3; } is an expression.

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.

Double curly braces in perl

I was looking at perl code online and came across something I hadn't seen before and can't find out what it's doing (if anything).
if($var) {{
...
}}
Does anyone know what the double curly braces mean?
There are two statements there. An "if" statement and a bare block. Bare blocks are loops that are executed exactly once.
say "a";
{
say "b";
}
say "c";
# Outputs a b c
But being loops, they do influence next, last and redo.
my $i = 0;
say "a";
LOOP: { # Purely descriptive (and thus optional) label.
++$i;
say "b";
redo if $i == 1;
say "c";
last if $i == 2;
say "d";
}
say "e";
# Outputs a b b c e
(next does the same as last since there is no next element.)
They are usually used to create a lexical scope.
my $file;
{
local $/;
open(my $fh, '<', $qfn) or die;
$file = <$fh>;
}
# At this point,
# - $fh is cleared,
# - $fh is no longer visible,
# - the file handle is closed, and
# - $/ is restored.
It's unclear why one was used here.
Alternatively, it could also be a hash constructor.
sub f {
...
if (#errors) {
{ status => 'error', errors => \#errors }
} else {
{ status => 'ok' }
}
}
is short for
sub f {
...
if (#errors) {
return { status => 'error', errors => \#errors };
} else {
return { status => 'ok' };
}
}
Perl peeks into the braces to guess if it's a bare loop or a hash constructor. Since you didn't provide the contents of the braces, we can't tell.
It's a trick usually employed with do, see chapter Statement Modifiers in perlsyn.
Probably the author wanted to jump out of the block with next or the like.
In case of if, they are probably equivalent to single braces (but it depends on what's inside the block and outside the if, cf.
perl -E ' say for map { if (1) {{ 1,2,3,4 }} } 1 .. 2'
). There are reasons to use double braces, though, with next or do, see perlsyn. For example, try running this several times:
perl -E 'if (1) {{ say $c++; redo if int rand 2 }}'
And try to replace double braces with single ones.
Without much more code, it's hard to say what they're being used for. It could be a typo, or it could be a naked block, see chapter 10.4 The Naked Block Control Structure in Learning Perl.
A naked block adds lexical scoping to variables within the block.
The {{ can be used to break out of an "if block". I have some code that contains:
if ($entry =~ m{\nuid: ([^\s]+)}) {{ # double brace so "last" will break out of "if"
my $uid = $1;
last if exists $special_case{$uid};
# ....
}}
# last breaks to here

Is there a cleaner way to conditionally 'last' out of this Perl loop?

Not really knowing Perl, I have been enhancing a Perl script with help from a friendly search engine.
I find that I need to break out of a loop while setting a flag if a condition comes true:
foreach my $element (#array) {
if($costlyCondition) {
$flag = 1;
last;
}
}
I know that the nicer way to use 'last' is something like this:
foreach my $element (#array) {
last if ($costlyCondition);
}
Of course, this means that while I can enjoy the syntactic sugar, I cannot set my flag inside the loop, which means I need to evaluate $costlyCondition once again outside.
Is there a cleaner way to do this?
you can use a do {...} block:
do {$flag = 1; last} if $costlyCondition
you can use the , operator to join the statements:
$flag = 1, last if $costlyCondition;
you can do the same with the logical && operator:
(($flag = 1) && last) if $costlyCondition;
or even the lower priority and:
(($flag = 1) and last) if $costlyCondition;
at the end of the day, there's no real reason to do any of these. They all do exactly the same as your original code. If your original code works and is legible, leave it like it is.
I agree with Nathan, that while neat looking code is neat, sometimes a readable version is better. Just for the hell of it, though, here's a horrible version:
last if $flag = $costly_condition;
Note the use of assignment = instead of equality ==. The assignment will return whatever value is in $costly_condition.
This of course will not make $flag = 1, but whatever $costly_condition is. But, since that needs to be true, so will $flag. To remedy that, you can - as Zaid mentioned in the comments - use:
last if $flag = !! $costly_condition;
As mentioned, pretty horrible solutions, but they do work.
One thought is to do the loop in a subroutine that returns different values depending on the exit point.
my $flag = check_elements(\#array);
# later...
sub check_elements {
my $arrayref = shift;
for my $ele (#$arrayref) {
return 1 if $costly_condition;
}
return 0;
}
This is possible, but highly not recommended: such tricks decrease readability of your code.
foreach my $element (#array) {
$flag = 1 and last if $costlyCondition;
}

Perl programming: continue block

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;