I've come across something odd while using a Perl script. It's about using a dot giving different results.
perlop didn't turn anything up, or perhaps I just blew past it. I was looking at Operator Precedence and Associativity
print "I'd expect to see 11x twice, but I only see it once.\n";
print (1 . 1) . "3";
print "\n";
print "" . (1 . 1) . "3\n";
print "Pluses: I expect to see 14 in both cases, and not 113, because plus works on numbers.\n";
print (1 . 1) + "3";
print "\n";
print "" + (1 . 1) + "3\n";
Putting quotes at the start is an acceptable workaround to get what I want, but what is happening here with the order of operations that I'm missing? What rules are there to be learned?
When you put the first argument to print in parentheses, Perl sees it as function call syntax.
So this:
print (1 . 1) . "3";
is parsed as this:
print(1 . 1) . "3";
or, equivalently:
(print 1 . 1) . "3";
Therefore, Perl prints "11", then takes the return value of that print call (which is 1 if it succeeded), concatenates 3 to it, and - since the whole expression is in void context - does absolutely nothing with the resulting 13.
If you run your code with warnings enabled (via -w on the command line or the use warnings; pragma), you will get these warnings identifying your error:
$ perl -w foo.pl
print (...) interpreted as function at foo.pl line 2.
print (...) interpreted as function at foo.pl line 6.
Useless use of concatenation (.) or string in void context at foo.pl line 2.
Useless use of addition (+) in void context at foo.pl line 6.
As Borodin points out in the comment below, you shouldn't rely on -w (or the in-code equivalent $^W); production code should always make use of the warnings pragma, preferably with use warnings qw(all);. While it wouldn't matter in this particular instance, you should also use strict;, although requesting modern features via useversion; for a Perl version of 5.11 or higher automatically turns on strict as well.
If a named operator (or a sub call) is followed by parens, those parens delimit the operands (or arguments).
print (1 . 1) . "3"; ≡ ( print(1 . 1) ) . "3";
print "" . (1 . 1) . "3"; ≡ print("" . (1 . 1) . "3");
Note that Perl would have alerted you of your problems had you been using (use strict; and) use warnings qw( all ); as you should.
print (...) interpreted as function at a.pl line 2.
print (...) interpreted as function at a.pl line 6.
Useless use of concatenation (.) or string in void context at a.pl line 2.
Useless use of addition (+) in void context at a.pl line 6.
I'd expect to see 11x twice, but I only see it once.
11
113
Pluses: I expect to see 14 in both cases, and not 113, because plus works on numbers.
11
Argument "" isn't numeric in addition (+) at a.pl line 8.
14
Related
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";
(edit) TL;DR: my problem was that I though the Win32 API defines were true integer constants (as in the platform SDK headers) while the Win32 Perl wrapper defines them as subs. Thus caused the one-liner parsing misunderstood.
While testing in a one-liner a call to Win32::MsgBox, I am puzzled by the following : giving that the possible arguments for MsgBox are the message, a sum of flags to chose the kind of buttons (value 0..5) and message box icon "constants" (MB_ICONSTOP, ...) and the title
calling perl -MWin32 -e"Win32::MsgBox world, 4+MB_ICONQUESTION, hello" gives the expected result
while the looking similar code perl -MWin32 -e"Win32::MsgBox world, MB_ICONQUESTION+4, hello" is wrong
I first though that it comes from my lack of parenthesis, but adding some perl -MWin32 -e"Win32::MsgBox (world, MB_ICONQUESTION+4, hello)" gives exactly the same wrong result.
I tried with a colleague to dig deeper and display the parameters that are passed to a function call (as the MB_xxx constants are actually subs) with the following code
>perl -Mstrict -w -e"sub T{print $/,'called T(#'.join(',',#_).'#)'; 42 }; print $/,'results:', join ' ,', T(1), T+1, 1+T"
that outputs
called T(#1#)
called T(##)
called T(#1,43#)
results:42 ,42
but I can't understand why in the list passed to join() the args T+1, 1+T are parsed as T(1, 43)...
B::Deparse to the rescue:
C:>perl -MO=Deparse -MWin32 -e"Win32::MsgBox world, MB_ICONQUETION+4, hello"
use Win32;
Win32::MsgBox('world', MB_ICONQUESTION(4, 'hello'));
-e syntax OK
C:>perl -MO=Deparse -MWin32 -e"Win32::MsgBox world, 4+MB_ICONQESTION, hello"
use Win32;
Win32::MsgBox('world', 4 + MB_ICONQUESTION(), 'hello');
-e syntax OK
The MB_ICONQUESTION call in the first case is considered a function call with the arguments +4, 'hello'. In the second case, it is considered as a function call with no arguments, and having 4 added to it. It is not a constant, it seems, but a function.
In the source code we get this verified:
sub MB_ICONQUESTION { 0x00000020 }
It is a function that returns 32 (00100000 in binary, indicating a bit being set). Also as Sobrique points out, this is a flag variable, so you should not use addition, but the bitwise logical and/or operators.
In your case, it just accepts any arguments and ignores them. This is a bit confusing if you are expecting a constant.
In your experiment case, the statement
print $/,'results:', join ' ,', T(1), T+1, 1+T
Is interpreted
print $/,'results:', join ' ,', T(1), T(+1, (1+T))
Because execution from right to left goes
1+T = 43
T +1, 43 = 42
T(1) = 42
Because plus + has higher precedence than comma ,, and unary + even higher.
To disambiguate, you need to do use parentheses to clarify precedence:
print $/,'results:', join ' ,', T(1), T()+1, 1+T
# ^^-- parentheses
As a general rule, one should always use parentheses with subroutine calls. In perldoc perlsub there are 4 calling notations:
NAME(LIST); # & is optional with parentheses.
NAME LIST; # Parentheses optional if predeclared/imported.
&NAME(LIST); # Circumvent prototypes.
&NAME; # Makes current #_ visible to called subroutine.
Of which in my opinion, only the first one is transparent, and the other ones a bit obscure.
This is all to do with how you're invoking T and how perl is interpreting the results.
If we deparse your example we get:
BEGIN { $^W = 1; }
sub T {
use strict;
print $/, 'called T(#' . join(',', #_) . '#)';
42;
}
use strict;
print $/, 'results:', join(' ,', T(1), T(1, 1 + T()));
This is clearly not what you've got in mind, but does explain why you get the result you do.
I would suggest in your original example - rather that + you may wish to consider using | as it looks very much like MB_ICONQUESTION is intended to be a flag.
So:
use strict;
use warnings;
use Win32 qw( MB_ICONQUESTION );
print MB_ICONQUESTION;
Win32::MsgBox( "world", 4 | MB_ICONQUESTION , "hello" );
Or
use strict;
use warnings;
use Win32 qw( MB_ICONQUESTION );
print MB_ICONQUESTION;
Win32::MsgBox( "world", MB_ICONQUESTION | 4 , "hello" );
Produce the same result.
This is because of precence when invoking subroutines without brackets - you can do:
print "one", "two";
And both are treated as arguments to print. Perl assumes that arguments after a sub are to be passed to it.
+4 is enumerated as an argument, and passed to T.
sub test { print #_,"\n";};
test 1;
test +1;
If we deparse this, we see perl treats it as:
test 1;
test 1;
So ultimately - there is a bug in Win32 that you have found, that would be fixable by:
sub MB_ICONQUESTION() {0x00000020}
Win32::MsgBox "world", 4 + MB_ICONQUESTION, "hello";
Win32::MsgBox "world", MB_ICONQUESTION + 4, "hello";
Or perhaps:
use constant MB_ICONQUESTION => 0x00000020;
Or as noted - the workaround in your code - don't use + and instead use | which is going to have the same result for bit flag operations, but because of operator precedence is never going to be passed into the subroutine. (Or of course, always specify the parenthesis for your constants)
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;
Instead of writing:
#holder = split /\./,"hello.world";
print #holder[0];
is it possible to just do a one-liner to just get the first element of the split? Something like:
print (split /\./,"hello.world")[0]
I get the following error when I try the second example:
print (...) interpreted as function at test.pl line 3.
syntax error at test.pl line 3, near ")["
You should have tried your hunch. That’s how to do it.
my $first = (split /\./, "hello.world")[0];
You could use a list-context assignment that grabs the first field only.
my($first) = split /\./, "hello.world";
To print it, use
print +(split /\./, "hello.world")[0], "\n";
or
print ((split(/\./, "hello.world"))[0], "\n");
The plus sign is there because of a syntactic ambiguity. It signals that everything following are arguments to print. The perlfunc documentation on print explains.
Be careful not to follow the print keyword with a left parenthesis unless you want the corresponding right parenthesis to terminate the arguments to the print; put parentheses around all arguments (or interpose a +, but that doesn't look as good).
In the case above, I find the case with + much easier to write and read. YMMV.
If you insist on using split for this then you could potentially be splitting a long string into multiple fields, only to discard all but the first. The third parameter to split should be used to limit the number of fields into which to divide the string.
my $string = 'hello.world';
print((split(/\./, $string, 2))[0]);
But I believe a regular expression better describes what you want to do, and avoids this problem completely.
Either
my $string = 'hello.world';
my ($first) = $string =~ /([^.]+)/;
or
my $string = 'hello.world';
print $string =~ /([^.]+)/;
will extract the first string of non-dot characters for you.
I get the following error when I try the second example:
"syntax error at test.pl line 3, near ")["
No, if you have warnings enabled as you should, you get:
print (...) interpreted as function at test.pl line 3.
syntax error at test.pl line 3, near ")["
which should be a big clue as to your problem.
In Perl most of my print statements take the form
print "hello." . "\n";
Is there a nice way to avoid keeping all the pesky "\n"s lying around?
I know I could make a new function such as myprint that automatically appends \n, but it would be nice if I could override the existing print.
Raku (Perl 6) has the say function that automatically appends \n.
You can also use say in Perl 5.10 or 5.12 if you add
use feature qw(say);
to the beginning of your program. Or you can use Modern::Perl to get this and other features.
See perldoc feature for more details.
You can use the -l option in the she-bang header:
#!/usr/bin/perl -l
$text = "hello";
print $text;
print $text;
Output:
hello
hello
See "-l[octnum]" in perlrun(1) for details.
If Perl 5.10+ is not an option, here is a quick and dirty approximation. It's not exactly the same, since say has some magic when its first arg is a handle, but for printing to STDOUT:
sub say {print #_, "\n"}
say 'hello';
The way you're writing your print statement is unnecessarily verbose. There's no need to separate the newline into its own string. This is sufficient.
print "hello.\n";
This realization will probably make your coding easier in general.
In addition to using use feature "say" or use 5.10.0 or use Modern::Perl to get the built in say feature, I'm going to pimp perl5i which turns on a lot of sensible missing Perl 5 features by default.
Perhaps you want to change your output record separator to linefeed with:
local $\ = "\n";
$ perl -e 'print q{hello};print q{goodbye}' | od -c
0000000 h e l l o g o o d b y e
0000014
$ perl -e '$\ = qq{\n}; print q{hello};print q{goodbye}' | od -c
0000000 h e l l o \n g o o d b y e \n
0000016
Update: my answer speaks to capability rather than advisability. I don't regard adding "\n" at the end of lines to be a "pesky" chore, but if someone really wants to avoid them, this is one way. If I had to maintain a bit of code that uses this technique, I'd probably refactor it out pronto.
Here's what I found at https://perldoc.perl.org/perlvar.html:
$\
The output record separator for the print operator. If defined, this value is
printed after the last of print's arguments. Default is undef.
You cannot call output_record_separator() on a handle, only as a static method.
See IO::Handle.
Mnemonic: you set $\ instead of adding "\n" at the end of the print. Also, it's
just like $/ , but it's what you get "back" from Perl.
example:
$\ = "\n";
print "a newline will be appended to the end of this line automatically";
In Raku (Perl 6) there is, the say function.
If you're stuck with pre-5.10, then the solutions provided above will not fully replicate the say function. For example
sub say { print #_, "\n"; }
Will not work with invocations such as
say for #arr;
or
for (#arr) {
say;
}
... because the above function does not act on the implicit global $_ like print and the real say function.
To more closely replicate the perl 5.10+ say you want this function
sub say {
if (#_) { print #_, "\n"; }
else { print $_, "\n"; }
}
Which now acts like this
my #arr = qw( alpha beta gamma );
say #arr;
# OUTPUT
# alphabetagamma
#
say for #arr;
# OUTPUT
# alpha
# beta
# gamma
#
The say builtin in perl6 behaves a little differently. Invoking it with say #arr or #arr.say will not just concatenate the array items, but instead prints them separated with the list separator. To replicate this in perl5 you would do this
sub say {
if (#_) { print join($", #_) . "\n"; }
else { print $_ . "\n"; }
}
$" is the global list separator variable, or if you're using English.pm then is is $LIST_SEPARATOR
It will now act more like perl6, like so
say #arr;
# OUTPUT
# alpha beta gamma
#
As requested:
sub myprint { print #_, "\n"; }
myprint "foo", 3 . 'bar'
And what was the actual problem?
Maybe try understanding the language you have before trying to change it.
Man perlvar(1) says: "Within a subroutine the array #_ contains the parameters passed to that subroutine."