Hi! Please consider following me on twitter: @hanekomu.
2010年03月18日
Boolean normalization using the logical negation operator
The perlsyn manpage says the following about truth and
falsehood:
The number 0, the strings '0' and '', the empty list "()", and "undef" are all false in a boolean context. All other values are true. Negation of a true value by "!" or "not" returns a special false value. When evaluated as a string it is treated as '', but as a number, it is treated as 0.
Sometimes you want to normalize a value so that it is 1 if it is a true value and 0 if it is a false value. This is sometimes called boolean normalization.
Usually you will see something like this:
my $b = $value ? 1 : 0;
There is an easier way. You could make use of the logical negation operator,
!. When it is applied to a true value, it returns a dual value
that is the number zero in numerical context and the empty string in string
context. When the logical negation operator is applied to a false value, it
returns a dual value that is the number 1 in numerical context and the string
"1" in string context. Let's see this in detail:
$ perl -MDevel::Peek -le'Dump !42' SV = PVNV(0x802000) at 0x12fef8 REFCNT = 2147483647 FLAGS = (IOK,NOK,POK,READONLY,pIOK,pNOK,pPOK) IV = 0 NV = 0 PV = 0x200160 ""\0 CUR = 0 LEN = 4 $ perl -MDevel::Peek -le'Dump !0' SV = PVNV(0x802014) at 0x12ff08 REFCNT = 2147483646 FLAGS = (IOK,NOK,POK,READONLY,pIOK,pNOK,pPOK) IV = 1 NV = 1 PV = 0x200170 "1"\0 CUR = 1 LEN = 4
By using the logical negation operator twice you can therefore simplify the above statement to:
my $b = !!$value;
And if you wanted the opposite boolean normalization, it's even easier:
my $b = $value ? 0 : 1;
becomes:
my $b = !$value;
posted at: 08:22 | path: /dev | permalink | 11 comments | 0 trackbacks
Ed Avis wrote at 2010-03-18 12:04:
Unfortunately, if you are after a '0' or '1' value, this doesn't always work:
my $v = ''; my $normalized = !!$v; print "normalized: '$v'\n";
This prints normalized: '' instead of normalized: '0'.
As you note in your article, what you get back is not 0 for false, but a weird magic value that is the empty string or zero depending on how it's used. If you want to get 0 or 1, no ifs and no buts, then
$normalized = $v ? 1 : 0;
is the way to do it.
I got tripped up by this myself yesterday when generating a URI with a boolean 0 or 1 flag in it. It took a while to figure out why my 0 value returned from the 'not' operator had been turned into an empty string.
hanekomu wrote at 2010-03-18 12:12:
Yes, it depends on the context in which you use the value.
Jeff wrote at 2010-03-18 12:15:
You can also use the or assignment operator:
$foo ||= 0;
hanekomu wrote at 2010-03-18 12:23:
Jeff, this wouldn't normalize a true value to 1, but leave it as it is. 42 || 0 is still 42.
fREW Schmidt wrote at 2010-03-18 15:38:
Were you in the #dbix-class channel yesterday? I was arguing that people should use this, but most people disagreed and thought ? 1 : 0; was more clear.
scaldwell wrote at 2010-03-18 17:35:
This will give you 0 and not an empty string:
$normalized = !!$v+0;
but I think
$normalized = $v ? 1 : 0;
is cleaner
hanekomu wrote at 2010-03-18 18:33:
fREW: I might have been... :)
scaldwell: agreed - !! is elegant if it works for you, but !!+0 is ugly.
chansen wrote at 2010-03-26 14:20:
$normalized = $v & 1;
Short and concise =)
hanekomu wrote at 2010-03-26 19:35:
chansen: This doesn't seem to work: 42 & 1 == 0
chansen wrote at 2010-03-26 22:06:
No that wont work.
0b01010100 (decimal 42) & 0b10000000 (decimal 1) = 0
!!0 & 1 == 0
!!1 & 1 == 1
0 & 1 == 0
1 & 1 == 1
Comments are closed for this story.
Trackbacks are closed for this story.
Zloyrusskiy wrote at 2010-03-18 09:54: