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 automatically. 1. Literal "nothing" is always false - C in scalar context - empty list, either implicit C or explicit C<()> NB: a non-empty list is always true, even if the elements are false like in C 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 to make objects false. =cut =head2 Boolean return values Most Boolean operators like C for C 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 objects) ); 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();