Why does this (mostly) empty Perl subroutine return an empty string? - perl

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.

Related

Assign number to variable in perl subroutine [assign returned list to variables]

I am trying to call a multiple variable from a subroutine but when i try to print, it just print only one variable.
In python the script will be f=mode()[0] and b=mode()[1] and it works.
subroutinefile a.pl
sub mode() {
my ($f, $b);
$f=41;
$b=2;
return ($f,$b);
}
And another file that calls the a.pl
use strict;
use warnings;
require 'a.pl';
my ($f,$b);
$f= mode(0);
$b= mode(1);
print "$f\n";
print "$b\n";
The problem is it only prints 2 for both f and b.
You have passed arguments to subroutine: $f = mode(0); $b = mode(1);.
Try the following:
my ($f,$b) = mode();
Your subroutine returns a fixed length list. You may assign values in the list to your variables.
The problem is the mismatch between the definition of the subroutine (no parameters) and the way you are trying to call it (one argument).
Change those calls to this and it works:
$f= (mode())[0];
$b= (mode())[1];
Think there is a syntactical difference between getting an array index FROM the return set versus sending an argument; function(xxx) sends the argument, (function()[])is using an array slice of the return.

What is "Use of unitialized value $. in range (or flip)" trying to tell me in Perl

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";

What is the definition of stringwise equality in Perl?

I was curious about the results of the following perl snippets:
my $var1 ;
my $var2 ;
if( $var1 eq $var2 ) {
print "yes";
} else {
print "no";
}
and
my $var1 ;
my $var2 = "";
if( $var1 eq $var2 ) {
print "yes";
} else {
print "no";
}
They turn out to be yes(Perl 5.16).
Unlike javascript specificaton, there is clear description of Equality Comparison Algorithm(http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3), the perl doc for Equality Operators says:
Binary "eq" returns true if the left argument is stringwise equal to the right argument.
But what is the definition of stringwise equality?
You're not having a problem with the definition of stringwise equality. What you don't seem to have wrapped your head arround yet is the concept of stringification. In this particular case, undef stringifies to ''.
Perl has monomorphic operators (mostly) and contextually polymorphic data types.
What this means is that it is the operator that dictates how the data is represented. The eq operator is for stringwise equality. The == operator is for numeric equality. When == is applied to strings, they are treated as numbers. When eq is applied to numbers, they are treated as strings. Each of these internal transformations follow a very specific set of rules.
Thus, when you apply a stringwise operator to a pair of values, those values will be treated as strings. When you apply a numeric operator to a pair of values, they will be treated as numbers.
In your second example, $var1 contains undef, and $var2 contains an empty string. When you compare $var1 eq $var2, the rules of stringification are applied to the operands. $var2 is already a string. But $var1 must be stringified before it can be compared. The rule of stringification for an undefined value is to treat it as an empty string. So $var1 eq $var2 is seen by the eq operator as '' eq '', which is to say, empty string eq empty string.
TRUE.
Note: In modern versions of Perl, using an undefined scalar variable in a stringwise operation only results in stringification for the duration of the operation; the underlying data in the container isn't altered. (see perldata).
perldata is one resource on this topic.
Your definition question has been answered by DavidO, but the (probably) most important piece of information is in TLP's comment - you should always use these two at the top of every script:
use strict;
use warnings;
The one relevant in this case is use warnings, which will produce the following warning:
Use of uninitialized value $var1 in string eq
Some programs go even further - this will cause the program to die because of this:
use warnings FATAL => 'all';
You are comparing variables using eq, which means you are doing a string comparison. A string comparison expects two strings, but you are providing an undefined variable (variable declared, but not defined, so not a string). This is not clean because you are using that operator with invalid input. If you wanted to know if the variables differ, not just the strings they (may or may not) represent, you would not use a string comparison like that.
It works because Perl knows that you want to compare strings (eq) so it assumes you don't care about the fact that a variable has no value assigned. The undefined variable is converted into an empty string (temporarily), which is then compared. (Actually, the variable itself isn't converted, it's still undef after the comparison, but that's not important.)
Of course, this assumption could be wrong and there might be a bug in your code. So it'd be better to check for invalid input before comparing it.
You don't care about the difference between undef and '' (and you know why).
You could explicitly compare empty strings instead of undef. Another programmer who is reading your code will know what's going on (won't assume there's a bug).
if (($var1 // q()) eq ($var2 // q()))
...
In many cases, you might actually care about undef.
For example, your script might take some input (maybe a hash) and if that input variable is an empty string, that would be okay, but if it's undef (maybe not found in the input hash), that would be an error.
if (!defined($var1))
{
die "Input data missing, can't continue!";
}
if ($var1 eq $var2)
...

Perl argument parsing into functions when no parentheses are used

I have the following test code:
sub one_argument {
($a) = #_;
print "In one argument: \$a = $a\n";
return "one_argument";
}
sub mul_arguments {
(#a) = #_;
return "mul_argument";
}
print &one_argument &mul_arguments "something", "\n";
My goal is to be able to understand a bit better how perl decides which arguments to go into each function, and to possibly clear up any misunderstandings that I might have. I would've expected the above code to output:
In one argument: mul_argument
one_argument
However, the below is output:
Use of uninitialized value $a in concatenation (.) or string at ./test.pl line 5.
In one argument: $a =
mdd_argument
I don't understand where 'mdd_argument' comes from (Is it a sort of reference to a function?), and why one_argument receives no arguments.
I would appreciate any insight as to how perl parses arguments into functions when they are called in a similar fashion to above.
Please note that this is purely a learning exercise, I don't need the above code to perform as I expected, and in my own code I wouldn't call a function in such a way.
perldoc perlsub:
If a subroutine is called using the & form, the argument list is optional, and if omitted, no #_ array is set up for the subroutine: the #_ array at the time of the call is visible to subroutine instead. This is an efficiency mechanism that new users may wish to avoid.
In other words, in normal usage, if you use the &, you must use parentheses. Otherwise, the subroutine will be passed the caller's #_.
The mysterious "mdd" is caused because &one_argument doesn't have any arguments and perl is expecting an operator to follow it, not an expression. So the & of &mul_arguments is actually interpreted as the stringwise bit and operator:
$ perl -MO=Deparse,-p -e 'sub mul_arguments; print &one_argument &mul_arguments "something", "\n"'
print((&one_argument & mul_arguments('something', "\n")));
and "one_argument" & "mul_arguments" produces "mdd_argument".

Where is the error "Use of uninitialized value in string ne" coming from?

Where is the uninitialised value in the below code?
#!/usr/bin/perl
use warnings;
my #sites = (undef, "a", "b");
my $sitecount = 1;
my $url;
while (($url = $sites[$sitecount]) ne undef) {
$sitecount++;
}
Output:
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
You can't use undef in a string comparison without a warning.
if ("a" ne undef) { ... }
will raise a warning. If you want to test if a variable is defined or not, use:
if (defined $var) { ... }
Comments about the original question:
That's a strange way to iterate over an array. The more usual way of doing this would be:
foreach my $url (#sites) { ... }
and drop the $sitecount variable completely, and don't overwrite $url in the loop body. Also drop the undef value in that array. If you don't want to remove that undef for some reason (or expect undefined values to be inserted in there), you could do:
foreach my $url (#sites) {
next unless defined $url;
...
}
If you do want to test for undefined with your form of loop construct, you'd need:
while (defined $sites[$sitecount]) {
my $url = $sites[$sitecount];
...
$sitecount++;
}
to avoid the warnings, but beware of autovivification, and that loop would stop short if you have undefs mixed in between other live values.
The correct answers have already been given (defined is how you check a value for definedness), but I wanted to add something.
In perlop you will read this description of ne:
Binary "ne" returns true if the left argument is stringwise not equal
to the right argument.
Note the use of "stringwise". It basically means that just like with other operators, such as ==, where the argument type is pre-defined, any arguments to ne will effectively be converted to strings before the operation is performed. This is to accommodate operations such as:
if ($foo == "1002") # string "1002" is converted to a number
if ($foo eq 1002) # number 1002 is converted to a string
Perl has no fixed data types, and relies on conversion of data. In this case, undef (which coincidentally is not a value, it is a function: undef(), which returns the undefined value), is converted to a string. This conversion will cause false positives, that may be hard to detect if warnings is not in effect.
Consider:
perl -e 'print "" eq undef() ? "yes" : "no"'
This will print "yes", even though clearly the empty string "" is not equal to not defined. By using warnings, we can catch this error.
What you want is probably something like:
for my $url (#sites) {
last unless defined $url;
...
}
Or, if you want to skip to a certain array element:
my $start = 1;
for my $index ($start .. $#sites) {
last unless defined $sites[$index];
...
}
Same basic principle, but using an array slice, and avoiding indexes:
my $start = 1;
for my $url (#sites[$start .. $#sites]) {
last unless defined $url;
...
}
Note that the use of last instead of next is the logical equivalent of your while loop condition: When an undefined value is encountered, the loop is exited.
More debugging: http://codepad.org/Nb5IwX0Q
If you, like in this paste above, print out the iteration counter and the value, you will quite clearly see when the different warnings appear. You get one warning for the first comparison "a" ne undef, one for the second, and two for the last. The last warnings come when $sitecount exceeds the max index of #sites, and you are comparing two undefined values with ne.
Perhaps the message would be better to understand if it was:
You are trying to compare an uninitialized value with a string.
The uninitialized value is, of course, undef.
To explicitely check if $something is defined, you need to write
defined $something
ne is for string comparison, and undef is not a string:
#!/usr/bin/perl
use warnings;
('l' ne undef) ? 0 : 0;
Use of uninitialized value in string ne at t.pl line 3.
It does work, but you get a [slightly confusing] warning (at least with use warnings) because undef is not an "initialized value" for ne to use.
Instead, use the operator defined to find whether a value is defined:
#!/usr/bin/perl
use warnings;
my #sites = (undef, "a", "b");
my $sitecount = 1;
my $url;
while (defined $sites[$sitecount]) { # <----------
$url = $sites[$sitecount];
# ...
$sitecount++;
}
... or loop over the #sites array more conventionally, as Mat explores in his answer.