I like perl the more I am getting into it but I had a question about a line I saw in a subroutine in a module I am looking through.
my $var = 1;
....
....
....
....
$var;
What throws me is just seeing that $var all by itself on a line. Is that just a roundabout way of returning 1 ?
Many thanks!
Jane
In perl the value of a block is the value of the last expression in the block. That is just a shorthand for return $var.
EDIT: Purists point out that that blocks in general do not return values (like they do in Scala, for example) so you can't write:
my $x = if (cond) { 7 } else { 8 }; # wrong!
The implicit return value of a subroutine, eval or do FILE is the last expression evaluated. That last expression can be inside a block, though:
sub f {
my $cond = shift;
if ($cond) { 7 } else { 8 } # successfully returns 7 or 8 from f()
}
There is the superficial appearance of the if/else blocks returning a value, even though, strictly speaking, they don't.
Quoting the last line of perldoc -f return:
In the absence of an explicit return, a subroutine, eval, or do FILE automatically returns the value of the last expression evaluated.
Related
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.
I noticed that if I replace the if-else statement I'm using with a ternary operator I end getting a compilation error when I try and run my code. I believe the culprit is the foreach() loop I have inside my if-else. Do you know why the ternary operator isn't behaving the same as the if-else construct in this instance?
My code looks like this
#!/program/perl_v5.6.1/bin/perl5.6.1
use strict;
use warnings;
my $fruits_array_ref = &get_fruits();
if($fruits_array_ref != 0) {
print("$_ is a fruit.\n") foreach(#$fruits_array_ref);
}
else {
print("Maybe you like vegetables?\n");
}
sub get_fruits() {
my #fruit_list;
my $shopping_list = "/home/lr625/grocery_items";
open(my $shopping_list_h, "<", $shopping_list) or die("Couldn't open.\n");
while(my $line = <$shopping_list_h>) {
next if $line =~ /^\#/;
chomp($line);
push(#fruit_list, $line);
}
close($shopping_list_h) or die("Couldn't close.\n");
scalar(#fruit_list) > 0 ? return(\#fruit_list) : return(0);
}
My data in the grocery list looks like
# this is a header
# to my grocery list
apple
banana
grape
orange
I'm replacing the if-else with a ?: operator to look like this now in the main function.
my $fruits_array_ref = &get_fruits();
$fruits_array_ref != 0 ? print("$_ is a fruit.\n") foreach(#$fruits_array_ref) : print("Maybe you like vegetables?\n");
Also, for reference my error says.
syntax error at test.pl line 8, near ") foreach"
Execution of test.pl aborted due to compilation errors.
if-else is a flow control structure, ?-: is an operator that takes expressions as operands. foreach is a flow control structure, not an expression.
You can turn any block of code into an expression by using do:
$fruits_array_ref != 0
? do { print "$_ is a fruit.\n" for #$fruits_array_ref }
: print "Maybe you like vegetables?\n";
But why?
The other answers already pointed out that you can't use the ternary operator the way you tried. For the sake of completeness and to give you some sensible use cases, take a look at the following examples:
#1: Used as a subroutine argument
testSub($var eq 'test' ? 'foo' : 'bar');
Here you can see how the subroutine testSub is called with the argument foo if $var equals the string test. Otherwise testSub will be called with bar. This is useful because you cannot use an if-else structure as a sub argument.
#2: Used for conditional assignment
my $result = $var eq 'test' ? 'foo' : 'bar'; # $result will contain 'foo' or 'bar'
The ternary operator is not meant as a simple replacement to an if-else structure. Since it returns a value (here either foo or bar) it makes sense to also use this value. If you don't intend to use the returned value, you should go for the usual if-else instead.
The foreach statement modifier can only be used at the end of a statement.
Why are you using ?:? You would normally only do that if you wanted a single result.
You could wrap the print...foreach... in a do {...}, or you could use map instead of foreach. Or just leave it as an if/else.
The ternary operator takes arguments before ? and :, see in perlop. It can evaluate an expression and use its result for this. But a loop is not an expression and cannot 'run' inside.
For a demonstration -- you could, if you insisted, call a function which will as a side effect print
sub greet { say "hello" for 1..3 }
my $x = 1;
($x == 1) ? greet() : say "bye";
Actualy doing this in production code is a different matter and would likely be a bad idea. The whole point would be to rely entirely on side effects, opposite to what we normally want to do.
To explain my comment above -- the main point of the ternary operator is to return a value, with a choice between two values, in one statement. While it is "equivalent" to an if-else, its use is (ideally) meant to be very different. So doing some other processing inside the ?: arguments, in any way, is really an abuse of notation, a side-effect, since they are intended to produce a value to be returned. Printing out of it is opposite to the idea of producing and returning a value. This is not a criticism, the operator is used often and by many as a syntactic shortcut.
In this sense I would recommend to revert to an if-else for doing what is shown.
I am currently attempting to document a Perl script in preparation for converting it to .NET. I have no prior experience in Perl before now, however I was managing to get through it with a lot of Google-fu. I have run into a single line of code that has stopped me as I am unsure of what it does. I've been able to figure out most of it, but I'm missing a piece and I don't know if it's really that important. Here is the line of code:
eval { if(defined $timeEnd && defined $timeStart){}; 1 } or next;
I know that defined is checking the variables $timeEnd and $timeStart to see if they are null/nothing/undef. I also believe that the eval block is being used as a Try/Catch block to trap any exceptions. The line of code is in a foreach loop so I believe the next keyword will continue on with the next iteration of the foreach loop.
The part I'm having difficulty deciphering is the {};1 bit. My understanding is that the ; is a statement separator in Perl and since it's not escaped with a backslash, I have no idea what it is doing there. I also don't know what the {} means. I presume it has something to do with an array, but it would be an empty array and I don't know if it means something special when it is directly after an if() block. Lastly, I no idea what a single integer of 1 means and is doing there at the end of an eval block.
If someone could break that line of code down into individual parts and their definitions, I would greatly appreciate it.
Bonus: If you can give me a .NET conversion, and how each Perl bit relates to it, I will most certainly give you my internet respects. Here's how I would convert it to VB.NET with what I know now:
For each element in xmlList 'This isn't in the Perl code I posted, but it's the `foreach` loop that the code resides in.
Try
If Not IsNothing(timeEnd) AND Not IsNothing(timeStart) then
End If
Catch ex as Exception
Continue For
End Try
Next
Ignoring elsif and else clasuses, the syntax of an if statement is the following:
if (EXPR) BLOCK
The block is executed if the EXPR evaluates to something true. A block consists of a list of statements in curly braces. The {} in your code is the block of the if statement.
It's perfectly valid for blocks to be empty (to contain a list of zero statements). For example,
while (s/\s//) { }
is an inefficient way of doing
s/\s//g;
The thing is, the condition in the following has no side-effects, so it's quite useless:
if(defined $timeEnd && defined $timeStart){}
It can't even throw an exception![1] So
eval { if(defined $timeEnd && defined $timeStart){}; 1 } or next;
is equivalent to
eval { 1 } or next;
which is equivalent to[2]
1 or next;
which is equivalent to
# Nothing to see here...
Technically, it can if the variables are magical.
$ perl -MTie::Scalar -e'
our #ISA = "Tie::StdScalar";
tie(my $x, __PACKAGE__);
sub FETCH { die }
defined($x)
'
Died at -e line 4.
I doubt the intent is to check for this.
Technically, it also clears $#.
eval{} returns result of last expresion (1 in your example) or undef if there was an exception. You can write same code as,
my $ok = eval {
if (defined $timeEnd && defined $timeStart){};
1
};
$ok or next;
From perldoc -f eval
.. the value returned is the value of the last expression evaluated inside the mini-program; a return statement may be also used, just as with subroutines.
If there is a syntax error or runtime error, or a die statement is executed, eval returns undef in scalar context or an empty list in list context, and $# is set to the error message
Here is a Perl program:
use strict;
use warnings;
use Data::Dumper;
sub f {
foreach (()) { }
}
print Dumper(f());
This outputs:
$VAR1 = '';
Since no value is explicitly returned from f, and no expressions are evaluated inside it, shouldn't the result be undef? Where did the empty string come from?
It hasn't quite returned the empty string; it has returned "false", an internal Perl value (called PL_no). This false value is numerically zero but stringily empty. Data::Dumper can't represent it directly as PL_no and so chooses a representation which will work.
Other ways you can generate it:
$ perl -MData::Dumper -E 'say Dumper(1 eq 2)'
$VAR1 = '';
Since no value is explicitly returned from f, and no expressions are evaluated inside it, shouldn't the result be undef?
Nope. perldoc perlsub says the return value is unspecified:
If no return is found and if the last statement is an expression, its value is returned. If the last statement is a loop control structure like a foreach or a while, the returned value is unspecified.
"Unspecified" is short for "we're not going to document the exact behavior because we could change it at any time and you shouldn't rely on it." Right now, it returns PL_no as LeoNerd explained; in a future Perl version, it could return undef, or something else altogether.
$hi = do_this('asdf');
sub do_this
{
$blob{'f'} = {
'k' => 'j'
};
}
print $hi->{'k'};
# prints j
since do_this doesn't return anything, how does it still print j?
http://perldoc.perl.org/functions/return.html
In the absence of an explicit return,
a subroutine, eval, or do FILE
automatically returns the value of the
last expression evaluated
All Perl 5 subroutines return the last value of the last statement executed.