Can someone explain what exactly the string "0 but true" means in Perl? As far as I understand, it equals zero in an integer comparison, but evaluates to true when used as a boolean. Is this correct? Is this a normal behavior of the language or is this a special string treated as a special case in the interpreter?
It's normal behaviour of the language. Quoting the perlsyn
manpage:
The number
0
, the strings'0'
and""
, the empty list()
, andundef
are all false in a boolean context. All other values are true. Negation of a true value by!
ornot
returns a special false value. When evaluated as a string it is treated as""
, but as a number, it is treated as0
.
Because of this, there needs to be a way to return 0
from a system call that expects to return 0
as a (successful) return value, and leave a way to signal a failure case by actually returning a false value. "0 but true"
serves that purpose.
perl -wle'print 0+"0 but false"'
prints "Argument "0 but false" isn't numeric in addition (+) at -e line 1.", but perl -wle'print 0+"0 but true"'
does not. –
Fourth Because it's hardcoded in the Perl core to treat it as a number. This is a hack to make Perl's conventions and ioctl
's conventions play together; from perldoc -f ioctl
:
The return value of
ioctl
(andfcntl
) is as follows:if OS returns: then Perl returns: -1 undefined value 0 string "0 but true" anything else that number
Thus Perl returns true on success and false on failure, yet you can still easily determine the actual value returned by the operating system:
$retval = ioctl(...) || -1; printf "System returned %d\n", $retval;
The special string
"0 but true"
is exempt from-w
complaints about improper numeric conversions.
perl -wle 'printf "%d\n", 4 > 5'
produces 0 sans complaint. –
Heater Additionally to what others said, "0 but true"
is special-cased in that it doesn't warn in numeric context:
$ perl -wle 'print "0 but true" + 3'
3
$ perl -wle 'print "0 but crazy" + 3'
Argument "0 but crazy" isn't numeric in addition (+) at -e line 1.
3
The value 0 but true
is a special case in Perl. Although to your mere mortal eyes, it doesn't look like a number, wise and all knowing Perl understands it really is a number.
It has to do with the fact that when a Perl subroutine returns a 0 value, it is assumed that the routine failed or returned a false value.
Imagine I have a subroutine that returns the sum of two numbers:
die "You can only add two numbers\n" if (not add(3, -2));
die "You can only add two numbers\n" if (not add("cow", "dog"));
die "You can only add two numbers\n" if (not add(3, -3));
The first statement won't die because the subroutine will return a 1
. That's good.
The second statement will die because the subroutine won't be able to add cow to dog.
And, the third statement?
Hmmm, I can add 3
to -3
. I just get 0
, but then my program will die even though the add
subroutine worked!
To get around this, Perl considers 0 but true
to be a number. If my add subroutine returns not merely 0, but 0 but true, my third statement will work.
But is 0 but true a numeric zero? Try these:
my $value = "0 but true";
print qq(Add 1,000,000 to it: ) . (1_000_000 + $value) . "\n";
print "Multiply it by 1,000,000: " . 1_000_000 * $value . "\n";
Yup, it's zero!
The index subroutine is a very old piece of Perl and existed before the concept of 0 but true was around. It is suppose to return the position of the substring located in the string:
index("barfoo", "foo"); #This returns 3
index("barfoo", "bar"); #This returns 0
index("barfoo", "fu"); #This returns ...uh...
The last statment returns a -1
. Which means if I did this:
if ($position = index($string, $substring)) {
print "It worked!\n";
}
else {
print "If failed!\n";
}
As I normally do with standard functions, it wouldn't work. If I used "barfoo" and "bar" like I did in the second statement, The else
clause would execute, but if I used "barfoo" and "fu" as in the third, the if
clause would execute. Not what I want.
However, if the index
subroutine returned 0 but true for the second statement and undef
for the third statement, my if
/else
clause would have worked.
You may also see the string "0E0" used in Perl code, and it means the same thing, where 0E0 just means 0 written in exponential notation. However, since Perl only considers "0", '' or undef as false, it evaluates to true in a boolean context.
It's hard-coded in Perl's source code, specifically in Perl_grok_number_flags in numeric.c.
Reading that code I discovered that the string "infinity" (case insensitive) passes the looks_like_number test too. I hadn't known that.
In an integer context, it evaluates to 0 (the numeric part at the beginning of the string) and is zero. In a scalar context, it's a non-empty value, so it is true.
if (int("0 but true")) { print "zero"; }
(no output)
if ("0 but true") { print "true"; }
(prints true)
0 means false in Perl (and other languages related to C). For the most part, that's a reasonable behavior. Other languages (Lua for instance) treat 0 as true and provide another token (often nil or false) to represent a non-true value.
One case where the Perl way doesn't work so well is when you want to return either a number or, if the function fails for some reason, a false value. For instance, if you write a function that reads a line from a file and returns the number of characters on the line. A common usage of the function might be something like:
while($c = characters_in_line($file)){
...
};
Notice that if the number of characters on a particular line is 0, the while loop will end before the end of the file. So the characters_in_line function should special case 0 characters and return '0 but true' instead. That way the function will work as intended in the while loop, but also return the correct answer should it be used as a number.
Note that this isn't a built in part of the language. Rather it takes advantage of Perl's ability to interpret a string as a number. So other stings are sometimes used instead. DBI uses "0E0", for instance. When evaluated in numeric context, they return 0, but in boolean context, false.
Things that are false:
""
."0"
.- Things that stringify to those.
"0 but true"
is not one of those, so it's not false.
Furthermore, Perl returns "0 but true"
where a number is expected in order to signal that a function succeeded even though it returned zero. sysseek is an example of such a function. Since the value is expected to be used as a number, Perl is coded to consider it to be a number. As a result, no warnings are issued when it's used as a number, and looks_like_number("0 but true")
returns true.
Other "true zeroes" can be found at http://www.perlmonks.org/?node_id=464548.
looks_like_number( "0 but true" )
is true, while looks_like_number( "0 but foobar" )
is false. –
Prague 0 but true
looks like a number but 0 but anything else
does not. They'll all evaluate to 0
in numeric context. I know what 0 but true
is for. –
Prague perl
. They felt they needed a string that was numerically zero, but also true and forgot they already had "0E0"
(or decided explaining why that was true and a the number 0 was too difficult for beginners). As to why "0 but true" needs to look like a number, well, there is a warning for non-numbers being used in numeric contexts. –
Slovak Another example of "0 but true":
The DBI module uses "0E0" as a return value for UPDATE or DELETE queries that didn't affect any records. It evaluates to true in a boolean context (indicating that the query was executed properly) and to 0 in a numeric context indicating that no records were changed by the query.
I just found proof that the string "0 but true" is actially built into the interpreter, like some people here already answered:
$ strings /usr/lib/perl5/5.10.0/linux/CORE/libperl.so | grep -i true
Perl_sv_true
%-p did not return a true value
0 but true
0 but true
When you want to write a function that returns either an integer value, or false or undef (i.e. for the error case) then you have to watch out for the value zero. Returning it is false and shouldn't indicate the error condition, so returning "0 but true" makes the function return value true while still passing back the value zero when math is done on it.
"0 but true" is a string just like any other but because of perl's syntax it can serve a useful purpose, namely returning integer zero from a function without the result being "false"(in perl's eyes).
And the string need not be "0 but true". "0 but false" is still "true"in the boolean sense.
consider:
if(x)
for x: yields:
1 -> true
0 -> false
-1 -> true
"true" -> true
"false" -> true
"0 but true" -> true
int("0 but true") ->false
The upshot of all of this is you can have:
sub find_x()
and have this code be able to print "0" as its output:
if($x = find_x)
{
print int($x) . "\n";
}
perl -wle 'print "true\n" if false;'
–
Greiner The string ``0 but true'' is still a special case:
for arg in "'0 but true'" "1.0*('0 but true')" \
"1.0*('0 but false')" 0 1 "''" "0.0" \
"'false'" "'Ja'" "'Nein'" "'Oui'" \
"'Non'" "'Yes'" "'No'" ;do
printf "%-32s: %s\n" "$arg" "$(
perl -we '
my $ans=eval $ARGV[0];
$ans=~s/^(Non?|Nein)$//;
if ($ans) {
printf "true: |%s|\n",$ans
} else {
printf "false: |%s|", $ans
};' "$arg"
)"
done
give the following: (note the ``warning''!)
'0 but true' : true: |0 but true|
1.0*('0 but true') : false: |0|
Argument "0 but false" isn't numeric in multiplication (*) at (eval 1) line 1.
1.0*('0 but false') : false: |0|
0 : false: |0|
1 : true: |1|
'' : false: ||
0.0 : false: |0|
'false' : true: |false|
'Ja' : true: |Ja|
'Nein' : false: ||
'Oui' : true: |Oui|
'Non' : false: ||
'Yes' : true: |Yes|
'No' : false: ||
... and don't forget to RTFM!
man -P'less +"/0 but [a-z]*"' perlfunc
... "fcntl". Like "ioctl", it maps a 0 return from the system call
into "0 but true" in Perl. This string is true in boolean
context and 0 in numeric context. It is also exempt from the
normal -w warnings on improper numeric conversions. ...
© 2022 - 2024 — McMap. All rights reserved.
"0E0"
another is like"0 but true"
. Instead of being hard coded it's a consequence of Perl's exponential notation. As a non-empty, non-zero string, it's true. As a number, it's 0. DBI uses it as a return value from things likeexecute
to indicate that the SQL statement worked, but no rows were affected. – Lianna