How does the different behavior of the unless- and "if !" statement influence the range-operator in scalar context? - perl

On http://novosial.org/perl/one-liner/ I found the following two one-liners. The outputs are different because the unless statement is different from if ! ( due to the associativity and precedence rules ).
cat file:
foo
bar
perl -ne 'print unless /^$/../^$/' file
foo
bar
perl -ne 'print if ! /^$/../^$/' file
foo
bar
How does the different behavior of the if !-statement make the second one-liner output one blank line?

As the linked article says, it's a matter of associativity and precedence...
print unless /^$/../^$/ is equivalent to print if !(/^$/../^$/)
print if ! /^$/../^$/ is equivalent to print if (!/^$/)../^$/
Note that the first negates the range expression, while the second negates the range's beginning condition, but not the range itself.

The answer, IMHO, is that there is no if ! statement in Perl: There is an if statement and there is a ! operator. The ! operator does not bind to if; it operates on its argument. If you start thinking in these terms, your life will be easier.
So, in your case, you have,
do_something() unless something;
and
do_something() if something-else;
Let's put in the normally invisible parentheses:
do_something() unless ( something );
and
do_something() if ( something-else );
In this case, you tried to write something-else so that the truth value of that condition is equivalent to the truth of something, but failed to take into account the fact that ! operator binds tightly.
That is why there is not.
Try
perl -ne 'print if not /^$/../^$/' file
See not in perldoc perlop:
Unary not returns the logical negation of the expression to its right. It's the equivalent of ! except for the very low precedence.

The range test will return true up to the time that the 2nd operand is true. It will then return false on the next call.
This snippet tells us what the range operator is returning
perl -ne "$x= ( ! /^$/../^$/ ) ; print qq/$x : $_/" range_test.TXT
which produces
1 : foo
2E0 :
:
:
1 : bar
So the first blank line generates a true response, the next one false

Related

Meaning of if(!$_)

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 [...]

Ternary operator doesn't allow iterative operator in it, but if-else does?

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.

Definition of empty {} after If() statement in eval{} statement

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

How exactly does Perl handle operator chaining?

So I have this bit of code that does not work:
print $userInput."\n" x $userInput2; #$userInput = string & $userInput2 is a integer
It prints it out once fine if the number is over 0 of course, but it doesn't print out the rest if the number is greater than 1. I come from a java background and I assume that it does the concatenation first, then the result will be what will multiply itself with the x operator. But of course that does not happen. Now it works when I do the following:
$userInput .= "\n";
print $userInput x $userInput2;
I am new to Perl so I'd like to understand exactly what goes on with chaining, and if I can even do so.
You're asking about operator precedence. ("Chaining" usually refers to chaining of method calls, e.g. $obj->foo->bar->baz.)
The Perl documentation page perlop starts off with a list of all the operators in order of precedence level. x has the same precedence as other multiplication operators, and . has the same precedence as other addition operators, so of course x is evaluated first. (i.e., it "has higher precedence" or "binds more tightly".)
As in Java you can resolve this with parentheses:
print(($userInput . "\n") x $userInput2);
Note that you need two pairs of parentheses here. If you'd only used the inner parentheses, Perl would treat them as indicating the arguments to print, like this:
# THIS DOESN'T WORK
print($userInput . "\n") x $userInput2;
This would print the string once, then duplicate print's return value some number of times. Putting space before the ( doesn't help since whitespace is generally optional and ignored. In a way, this is another form of operator precedence: function calls bind more tightly than anything else.
If you really hate having more parentheses than strictly necessary, you can defeat Perl with the unary + operator:
print +($userInput . "\n") x $userInput2;
This separates the print from the (, so Perl knows the rest of the line is a single expression. Unary + has no effect whatsoever; its primary use is exactly this sort of situation.
This is due to precedence of . (concatenation) operator being less than the x operator. So it ends up with:
use strict;
use warnings;
my $userInput = "line";
my $userInput2 = 2;
print $userInput.("\n" x $userInput2);
And outputs:
line[newline]
[newline]
This is what you want:
print (($userInput."\n") x $userInput2);
This prints out:
line
line
As has already been mentioned, this is a precedence issue, in that the repetition operator x has higher precedence than the concatenation operator .. However, that is not all that's going on here, and also, the issue itself comes from a bad solution.
First off, when you say
print (($foo . "\n") x $count);
What you are doing is changing the context of the repetition operator to list context.
(LIST) x $count
The above statement really means this (if $count == 3):
print ( $foo . "\n", $foo . "\n", $foo . "\n" ); # list with 3 elements
From perldoc perlop:
Binary "x" is the repetition operator. In scalar context or if the left operand is not enclosed in parentheses, it returns a string consisting of the left operand repeated the number of times specified by the right operand. In list context, if the left operand is enclosed in parentheses or is a list formed by qw/STRING/, it repeats the list. If the right operand is zero or negative, it returns an empty string or an empty list, depending on the context.
The solution works as intended because print takes list arguments. However, if you had something else that takes scalar arguments, such as a subroutine:
foo(("text" . "\n") x 3);
sub foo {
# #_ is now the list ("text\n", "text\n", "text\n");
my ($string) = #_; # error enters here
# $string is now "text\n"
}
This is a subtle difference which might not always give the desired result.
A better solution for this particular case is to not use the concatenation operator at all, because it is redundant:
print "$foo\n" x $count;
Or even use more mundane methods:
for (0 .. $count) {
print "$foo\n";
}
Or
use feature 'say'
...
say $foo for 0 .. $count;

What is the result of Perl's &&?

When I try this:
$a = 1;
$b = 2;
print ($a && $b) . "\n";
The result is 2. Why?
Quote perlop:
The "||", "//" and "&&" operators
return the last value evaluated
(unlike C's "||" and "&&", which
return 0 or 1).
The resulting 2 is considered true by Perl, so that when you use the && operator in a logical condition, everything works as expected. The added bonus is that you can use the logical operators in other contexts as well:
sub say_something {
say shift || 'default';
}
say_something('foo'); # prints 'foo'
say_something(); # prints 'default'
Or even as flow modifiers:
my $param = shift || die "Need param!";
-f $file && say "File exists.";
In the last two examples it’s good to realize that they could not work if the && and || operators did not short-circuit. If you shift a true value in on first line, there is no point evaluating the right side (die…), since the whole expression is true anyway. And if the file test fails on the second line, you don’t need to evaluate the right side again, since the overall result is false. If the logical operators insisted on evaluating the whole expression anyway, we could not use them this way.
That's how the && operator works: if the left-hand argument evaluates as true, the value of the expression is that of the value of the right-hand argument. This is covered on the perlop page.
However, you've also let yourself open to a much more subtle problem. You'll find that the newline doesn't get printed. This is because if you put an expression in brackets after print (or any other function name) the arguments passed to print are just those in the brackets. To get notice of this, make sure you switch on warnings. Put these lines at the top of each program:
#!/usr/bin/perl -w
use strict;
until you understand them enough to decide for yourself whether to continue with them. :-)
In your case, you'll get this:
print (...) interpreted as function at p line 7.
Useless use of concatenation (.) or string in void context at p line 7.
Because the && operator evaluates the right operand and returns the result when the left operand evaluates to true.