http://qs321.pair.com?node_id=1233207


in reply to How to get 0 to initialize a value

Hi Lady_Aleena!,

I think others have done a great job of addressing the central coding issue.
This post is in part a reaction to your statement: I can't simply add a +0 to it. I hope that I can clarify why you might want to +0 - that answer has nothing to do with "definedness", but might be interesting for you to know.

In Perl, a variable starts "life" as undefined. "my $x;" means that x is created (memory allocated), but has no value at all associated with it. It does not have a zero numeric value or an empty string or anything else - it has no value whatsoever. Zero is a completely fine and valid value. What "0" means is very well defined.

In Perl, a variable is not just a single memory location. There is a data structure associated with that variable containing a number of properties. When you assign a value to a Perl variable, it gets a string value. If that variable is then later used in a numeric way, Perl will create a numeric value for that variable. This of course is done so that further math calculations with that variable are WAY, WAY faster. For the most part this "magic" is completely transparent (except of course when it is not!).

I wrote some code that illustrates some of the main points. It is difficult in these examples to anticipate every possible scenario. But I think the code makes the main points.

Below in the first example, $string starts out with a string of characters, "00021". This what will happen when you read text DATA as you have done. One way to remove the leading zero'es is with a regex. That is completely fine.

Another way to "delete the leading zero'es" is to use $string in a math operation. The seemingly nonsensical operation of adding zero to $string does that. Internally, Perl now knows that $string is a "number". When you print it, there are no leading zeroes. Perl is using the numeric value for that variable instead of the string value.

There a certain types of data structures that I work with where +0 is a good idea because I'm going to use this value mathematially anyway. This also has the benefit (nor not), depending upon how you look at it of causing a run time error if the $string is not numeric. I show that as example 3 below. Perl will happily do "the best it can", but will report a run time warning in this situation (with use warnings;).

As it turns out, there is one very special case of a non-numeric string that will not produce a warning when used in math! Hard coded into Perl is the exception: "0 but true". That string will evaluate to numeric 0 when used in math, but will also evalue to "true" if that string is tested in a logical context (if ($string){}). Normally "0" evaluates to "false" in a logical operation.

There are certain situations where this behaviour is very useful. For example in the Perl DBI, I do an "SQL update" and I want to know a) did the command work? and b) how many rows were affected? The Perl DBI will return a single value of 0E0 if the command worked (logically true), but zero rows were affected. (0*10**0 = 0*1 = 0).

Starting in Perl 5.10, the // operator was introduced. This operator tests for "definedness".

I show 2 examples of how to set a variable to be some defined value if it is not currently defined. This is useful if say you run a regex to capture some match and that match does not succeed. Rather than some if logic perhaps with the ternary operator, you can just code: $potental_match //= ''; to cover a case where the regex failed to find the $potental_match and set that variable to a default value. I personally recommend the Perl 5.10 coding style because the science of computer programming has shown that any statement with an "if" in it is way, way more likely to contain an error than one without an if. This syntax is clear and works efficiently.

Hope this helped!

#!/usr/bin/perl use strict; use warnings; my $string = "00021"; $string=~ s/^0*//; # remove leading zero'es print "$string\n"; # prints: 21 $string = "00021"; $string += 0; # converts string to a numeric value print "$string\n"; # prints: 21 $string = "00021scores"; #$string += 0; # Warning: Argument "00021scores" isn't numeric i +n addition (+) print "$string\n"; # prints: 21 $string = "0 but true"; $string += 33; # Ha! A Perl special case! no warning! print "$string\n"; # prints: 33 $string = "0E0"; # "better" way for a "logically true but zero valu +e" $string += 33; # print "$string\n"; # prints: 33 my $nothing; #print "$nothing\n"; # Use of uninitialized value $nothing in concaten +ation (.) or string $nothing //= ''; # set $nothing to '' if it was undefined, else no + operation. print "$nothing\n"; # orint is ok, now.. my $nothing2; $nothing2 = '' if !defined $nothing2; # pre Perl 5.10 print "$nothing2\n";
Update:
As far as your code goes, I personally find this ternary within a ternary to be not easy to understand - that means error prone to write and error prone to read. A much more wordy if..elsif..else will compile to the same thing, and probably easier to understand and also for you to see that "undef" is a possible outcome!

Sometimes maximum performance is not needed. Just as one possibility, See the code below. Slightly slower because both regex expressions will be run for all cases, but I personally find the lengthier code easy to understand at a glance.

my $first_type = $first_book =~ /^M/ ? 'major' : $first_book =~ /^m/ +? 'mentioned' : undef; #could be coded as: my $first_type = 'major' if $first_book =~ /^M/; $first_type = 'mentioned' if $first_book =~ /^m/; $first_type //= 'unknown';