Hi! Please consider following me on twitter: @hanekomu.

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;

Write a comment | Bookmark and Share

posted at: 08:22 | path: /dev | permalink | 11 comments | 0 trackbacks

Zloyrusskiy wrote at 2010-03-18 09:54:

You're genius!

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.