use strict;
use warnings;
use Data::Dump qw/pp dd/;
use Scalar::Util qw/isdual/;
use Test::More;
=head1 Boolean Operations in Perl
=head2 True and False values
Perl has no boolean datatype but a "boolean context" where other
values evaluate to true or false.
Operands can be literal values like depending on context.
- strings
- numbers
Lists are converted to scalars
Perl variables have 3 main datatypes: Scalars, Arrays and Hashes.
The boolean context is just a specialized scalar context and the
literal empty list C<()> evaluates to false.
Arrays and Hashes are false iff they are empty.
Scalar values are internally represented
- strings
- integers
- float (double precission)
- references/objects
- undefined
But Perl allows to use them all interchangabliy by casting them automa
+tically.
1. Literal "nothing" is always false
- C<undef> in scalar context
- empty list, either implicit C<return;> or explicit C<()>
NB: a non-empty list is always true, even if the elements are false
like in C<return undef>
2. The neutral element of every internal datatype is FALSE:
- i.e. all literal zeroes for numbers 0, 0.0, 0e0
- the empty string ''
- empty arrays and hashes
NB: The "0" string is also false, because Perl treats 0 and "0" to be
the same scalar. But "0.0" or "0E0" are never false, these are literal
notations.
3. References including those blessed to objects are always true.
NB: You can treach with C<use overload> to make objects false.
=cut
=head2 Boolean return values
Most Boolean operators like C<!> for C<not> return a "default scalar"
as represntative for true and false.
These can be easily construted by double negation
false := !!0
true := !!1
But so called "Short-circuit Operators" change the control-flow.
They return the last evaluated side instead, which is guarantied to be
logically correct (later more)
=cut
my @false = (
!!0, # default
0, 0.0, 0e0, 0e10, "0", # scalar zeroes
'', # empty string
undef, # undef
!!(), # empty list
);
# NB: you can fake "false" objects by using =overload=
sub is_FALSE {
return unless isdual($_[0]) ;
my $num = $_[0];
my $str = $_[0];
return ($num == 0) && ($str eq "");
}
cmp_ok ( !!0, "==", 0, "!!0 is 0 in numeric context");
cmp_ok ( !!0, "eq", "", "!!0 is '' in string context");
ok ( is_FALSE(!!0));
# anything else is true
my @true = (
!!1, # default
1 ,
"A", "0abc", "0 but true", "0e0", # all strings except "0"
\0, [], {}, # refs (including blessed obje
+cts)
);
cmp_ok ( !!1, "==", 1, "!!1 == 1 in numeric context");
cmp_ok ( !!1, "eq", 1, "!!1 eq 1 in string context");
cmp_ok ( !!1, "eq", '1', "!!1 eq '1' in string context");
for my $x ( @true ) {
ok (
$x,
"true: ". pp($x)
);
}
for my $x ( @false ) {
ok (
! $x,
"false: ". pp($x)
);
}
#done_testing();
#exit;
=head2 short-circuit operators
=cut
my @values = (@false, @true);
warn pp \@values;
for my $x ( @values ) {
for my $y ( @values ) {
is(
($x || $y),
($x ? $x : $y),
"|| " .pp($x,$y) . " => " . pp ($x || $y)
);
}
}
for my $x ( @values ) {
for my $y ( @values ) {
is(
($x && $y),
(!$x ? $x : $y),
"&& ".pp($x,$y) . " => " . pp ($x && $y)
);
}
}
for my $x ( @values ) {
for my $y ( @values ) {
is(
($x // $y),
( (defined $x) ? $x : $y),
"// ".pp($x,$y) . " => " . pp ($x && $y)
);
}
}
done_testing();