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
Related
I have a script in Perl that is reading a file. At some point, the code utilize the following if statement inside a for loop:
for (my $i = 0; $i<10 ; $i++ ) {
$_ = <INPUT>;
if (!$_) {last;}
...
I am new in Perl, so I would like to know the meaning of !$_. In this example, $_ is a line of my file. So, what content the line should have to the if statement be true.
The if condition, what is inside (), is evaluated in a boolean scalar context to be tested for "truthiness." So if it's undef or '' (empty string) or 0 (or string "0") it's false.
That ! negates what follows it, so if (!$_) is true if $_ is false (undef or '' or 0 or "0"). However, in this case that $_ is assigned from <> operator so it'll always have a linefeed at the end -- unless the source for <> was exhausted in which case <> returns undef.
So, in this case, that if (!$_) tests for whether there is nothing more to read from INPUT, and exits the for loop with last if that is the case.
A few comments on the shown code.
That C-style for loop can also be written as for my $i (0..9), what is considered far nicer and more readable.† See foreach, and really follow links for flow-control key-words
The piece of code
$_=<INPUT>
if (!$_) { last; }
...
reads from INPUT filehandle and exits its loop (see last) once there is end-of-file. (That need not be an actual file but any resource readable via a filehandle.)
This is clumsy, to say the least; a common way of doing it is
while (<INPUT>) {
...
}
† So much so that even hard-core compiled languages now have it. The C++11 introduced the range-based for loop
for (auto var: container) ... // (really, const auto&), or auto&, or auto&&
and the standard reference linked above says
Used as a more readable equivalent to the traditional for loop [...]
Hello I am new to programming in perl I am trying to make a number adder (math) but it gives me 1 error here's my code:
sub main {
print("First: ");
$num1 = <STDIN>;
print("Second: ");
$num2 = <STDIN>;
$answer = $num1 + $num2;
print("$answer")
} else {
print("You have entered invalid arguments.")
}
main;
now obviously its not done but I get an error on ONLY else here is the error:
C:\Users\aries\Desktop>"Second perl.pl"
syntax error at C:\Users\aries\Desktop\Second perl.pl line 9, near "} else"
Execution of C:\Users\aries\Desktop\Second perl.pl aborted due to compilation errors.
please help (also I tried googling stuff still error)
Since you're new to Perl, I recommend you add strict and warnings at the top of your scripts. This will help identify common problems and potentially dangerous code.
The main problem with your code is that you've appended the else statement to your subroutine. Here is an example of your code as I think you intended it to be:
use strict;
use warnings;
use Scalar::Util qw(looks_like_number);
sub main {
print 'First :';
my $num1 = <STDIN>;
print 'Second: ';
my $num2 = <STDIN>;
if( looks_like_number($num1) && looks_like_number($num2) ) {
my $answer = $num1 + $num2;
print "$answer\n";
}
else {
die 'You have entered invalid arguments.';
}
}
main();
There are a few things I should note here about the differences between Perl and Python that I think will help you understand Perl better.
Unlike Python, Perl doesn't care about white space. It uses curly braces and semi-colons to indicate the end of blocks and statements and will happily ignore any white space that isn't in quotes.
The else statement appended to the subroutine won't work because Perl isn't designed to evaluate code blocks that way. A subroutine is simply a named code block that can be called at any other point in the script. Any error handling will need to be done inside of the subroutine rather than to it.
Perl is very context-sensitive and doesn't make a solid distinction between strings and integers in variables. If you write $var_a = 1, Perl will read it as an integer. $var_b = '1', it will read it as a string. But, you can still add them together: $var_c = ($var_a + $var_b), and Perl will make $var_c = 2.
This is another reason the else statement would not work as you've written it. Python would throw an error if you try to add non-integers, but Perl will just figure out how to combine them, and give you the result. If you try to add a letter and a number, Perl still won't fail, but it will warn you if you put "use warnings;" at the top of your script.
In the example, and as Dada mentioned, you can use the looks_like_number() method from the Scalar::Utils module to evaluate the variables as you had intended.
Apart from the syntax, if/else statements work the same way in Perl as in Python. The else-if is slightly different as is has an extra s:
if (condition) {
...
}
elsif (other condition) {
...
}
else {
...
}
In Perl, it's good practice to assign lexical scope to variables using the my function. Since Perl is so context-sensitive, this can help prevent unexpected behavior when moving between different scopes.
Single- and double-quotes have different uses in Perl. Single-quotes are read literally and double-quotes are interpolated, so if you want to combine variables and text together, you can skip concatenating the strings and just do: print "Got variable: $var\n";
Lastly, note that I added parentheses after the main subroutine call. This is another best practice to make it clearer that you are calling a subroutine as opposed to it being a bare word or a bad variable name.
I am trying to read a file,
while($line = $file_handle)
When I ran this code, the program hung.
I noticed to read the file using file handle, we need to used <>
while($line = <file_handle>)
The later code obviously ran.
Now I know operator <> is to read the file line by line, I want to know what exactly is happening when I dont provide <> operator?
Is it not able to find the end of the line ?or?
Thankyou
Given this code:
use warnings;
use strict;
open my $fh, '<', 'in.txt' or die $!;
while (my $x = $fh){
$DB::single=1;
print "$x\n";
}
when run in the debugger, you can see that $x now contains a GLOB (a copy of the file handle). On each iteration, $x takes the whole handle as an assignment, and causes an infinite loop, because the statement is always true. Because you're always assigning a true value (the handle), the while statement is effectively no different than writing while(1){....
perl -d script.pl
main::(x.pl:4): open my $fh, '<', 'in.txt' or die $!;
DB<1> s
main::(x.pl:6): while (my $x = $fh){
DB<1> s
main::(x.pl:7): $DB::single=1;
DB<1> x $x
0 GLOB(0xbfdae8)
-> *main::$fh
FileHandle({*main::$fh}) => fileno(6)
DB<2> s
main::(x.pl:7): $DB::single=1;
DB<2> x $x
0 GLOB(0xbfdae8)
-> *main::$fh
FileHandle({*main::$fh}) => fileno(6)
<$fh> essentially extracts a single line from the file handle, and when EOF is hit (or an error occurs), returns undef, which terminates the loop (because undef is false).
Short File is read by the <> operator so without it it is only an assignment, thus the infinite loop.
The while (...) { ... } checks the condition inside () and if true it executes the body in the block {}. It keeps doing this until the condition in () evaluates to what is understood as false (generally 0, '0', '', or undef). This is what the operator <> provides, for example, so we have an idiom
while (my $line = <$file_handle>) { ... }
The <> operator reads a line at each iteration from the resource that $file_handle is associated with and when it reaches end-of-file it returns undef so the loop terminates and the program execution continues at the next statement after the loop. The diamond operator <> is the operator form for the function readline. See I/O Operators in perlop. For all this to work $file_handle has to be a valid resource that can retrieve data.
Without the <> operator nothing is read from anywhere, but there is only an assignment. The code does the following. It copies the variable $file_handle into the variable $line. The return value of that operation in Perl is the value that ends up in $line, and if that is a 'truthy' value then the body { ... } is executed. The $file_handle clearly evaluates to 'true', otherwise the loop body would not execute even once and the program would continue. Thus $line is true, too. So if the $file_handle doesn't change in the body {...} of the loop the condition is always true.
Then whatever is in the body keeps being executed, without a reason for the loop to terminate, and it thus never returns control to the program. It's an infinite loop, and the program appears to hang.
Note that this is sometimes used deliberately and you may see code like
while (1) {
# Compute what is needed
# Recalculate the condition for when to stop
last if $condition_to_terminate;
}
This approach can be risky though, since the condition can get more and more complicated and an error could sneak in, in which case we end up with an infinite loop. There are usually clearer ways to control loops.
A completely different example is an event loop, where it is crucial to enter an infinite loop so that we can then wait for an event of some sort, at which point a particular action is taken. This is how GUI's work, for example, and a number of other systems.
For the example that 'hung':
while($line = $file_handle)
The elements $line = $file_handle is purely an assignment. At that point, your while is just checking that the assignment is truthy, i.e. that $line is not the number 0, the string 0, an empty string, or undef, which of course it's not -> hense you get an infinite loop.
I have the following code snippet in Perl:
my $argsize = #args;
if ($argsize >1){
foreach my $a ($args[1..$argsize-1]) {
$a =~ s/(.*[-+*].*)/\($1\)/; # if there's a math operator, put in parens
}
}
On execution I'm getting "Use of unitialized value $. in range (or flip) , followed by Argument "" isn't numeric in array element at... both pointing to the foreach line.
Can someone help me decipher the error message (and fix the problem(s))? I have an array #args of strings. The code should loop through the second to n't elements (if any exist), and surround individual args with () if they contain a +,-, or *.
I don't think the error stems from the values in args, I think I'm screwing up the range somehow... but I'm failing when args has > 1 element. an example might be:
<"bla bla bla"> <x-1> <foo>
The long and short of it is - your foreach line is broken:
foreach my $a (#args[1..$argsize-1]) {
Works fine. It's because you're using a $ which says 'scalar value' rather than an # which says array (or list).
If you use diagnostics you get;
Use of uninitialized value $. in range (or flip) at
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you
the name of the variable (if any) that was undefined. In some cases
it cannot do this, so it also tells you what operation you used the
undefined value in. Note, however, that perl optimizes your program
and the operation displayed in the warning may not necessarily appear
literally in your program. For example, "that $foo" is usually
optimized into "that " . $foo, and the warning will refer to the
concatenation (.) operator, even though there is no . in
your program.
You can reproduce this error by:
my $x = 1..3;
Which is actually pretty much what you're doing here - you're trying to assign an array value into a scalar.
There's a load more detail in this question:
What is the Perl context with range operator?
But basically: It's treating it as a range operator, as if you were working your way through a file. You would be able to 'act on' particular lines in the file via this operator.
e.g.:
use Data::Dumper;
while (<DATA>) {
my $x = 2 .. 3;
print Dumper $x;
print if $x;
}
__DATA__
line one
another line
third line
fourth line
That range operator is testing line numbers - and because you have no line numbers (because you're not iterating a file) it errors. (But otherwise - it might work, but you'd get some really strange results ;))
But I'd suggest you're doing this quite a convoluted way, and making (potentially?) an error, in that you're starting your array at 1, not zero.
You could instead:
s/(.*[-+*].*)/\($1\)/ for #args;
Which'll have the same result.
(If you need to skip the first argument:
my ( $first_arg, #rest ) = #args;
s/(.*[-+*].*)/\($1\)/ for #rest;
But that error at runtime is the result of some of the data you're feeding in. What you've got here though:
use strict;
use warnings;
my #args = ( '<"bla bla bla">', '<x-1>', '<foo>' );
print "Before #args\n";
s/(.*[-+*].*)/\($1\)/ for #args;
print "After: #args\n";
I'm new to Perl and I have a problem when reading a file line by line. I started with a tutorial which suggested using a while loop. That worked fine, however I wanted to have an option to break out of the loop in case of an error. I know I can use the "last" keyword, but I don't like that very much, I would like to include it in the while loop's boolean expression so it is easier to see on what conditions it stops. So I did
$error=0;
while ( (!$error) && (<MYFILE>) ) {
print $_;
...
}
Unfortunately, this doesn't work because $_ seems to contain a "uninitialized value". For some reason, when I change (!$error) to (!0), it works again. I would understand that it was not working had I used || because of lazy evaluation, but with && in this case both sides need to be evaluated so I don't understand why it doesn't initialize the line variable.
The magical assignment to $_ only occurs if there is nothing else in the loop:
while (<MYFILE>) {
print $_;
...
}
If you want the error test too - which is not recommended - then you need to do the assignment:
my $error = 0;
while (!$error && ($_ = <MYFILE>)) {
print;
$error = 1 if m/quit/i;
}
Don't forget to add:
use warnings;
use strict;
The reason why the version with '!0' worked is probably because Perl optimizes and recognizes that '!0' is always true and removes it from the loop, leaving just the I/O operator, which then assigns to $_.
NB: it is far better to use idiomatic Perl and the last statement:
while (<MYFILE>) {
print;
last if m/quit/i;
}
First, as Jonathan points out you should always write languages idiomatically unless you have a very good reason and here you don't.
Your larger problem is misunderstanding the while loop. The automatic assignment of the result of the angle operator to $_ only occurs if it is the only thing in the while conditional test. If there are multiple items in the conditional test they are evaluated and then discarded (Programming Perl pp. 80-81 and I/O Operators at Perldoc).