Lady_Aleena has asked for the wisdom of the Perl Monks concerning the following question:
I haven't written perl in a while, so I've forgotten how to tell perl that 0 is valid to initialize a value.
The warning Use of uninitialized value is bugging me a lot. I don't like having any warnings.
I have a list of characters who have appeared in one or more books. There is one character whose first book notation is the number 0. The split on line 10 is said to be uninitialized. On line 12, $first_book in the $first_type is also uninitialized. On line 13, the removal of the non-digit characters in the $first_book, also says $first_book is an uninitialized value. That value is in fact initialized. Because the first book can contain alphabetical characters, I can't simply add a +0 to it.
Any suggestions on how to tell perl that 0 initializes the string?
I did not come up with this notation system, but I can't think of anything to replace it that won't make the data too long. I feel silly asking this, but I can't think of anything right now. Also, if you know who Bink is or recognize the titles of the novels and want to ask me what I am doing; please use PMs. Thanks! 8)
use strict;
use warnings;
my @novel_list = map { chomp $_; $_ } <DATA>;
my $Bink_books = 'M1, M2, m3, m4, 6, m7, M9, m11, m13, 14, 17, m19, m2
+1, M22, m23, m31, 35, 36, m37, m40, m41';
my $Quan_books = '0';
for my $books ($Bink_books, $Quan_books) {
my @novels = split(/, /, $books); # $books is initialized for Quan e
+ven though the value is 0.
my $first_book = $novels[0];
my $first_type = $first_book =~ /^M/ ? 'major' : $first_book =~ /^m/
+ ? 'mentioned' : undef;
$first_book =~ s/\D//g;
my $first_novel = $novel_list[$first_book]; # was my $first_novel =
+$novel_list->[$first_book];
print "$first_novel is the first novel the character was in.\n";
}
__DATA__
Other source
A Spell for Chameleon
The Source of Magic
Castle Roogna
Centaur Aisle
Ogre, Ogre
Night Mare
Dragon on a Pedestal
Crewel Lye: A Caustic Yarn
Golem in the Gears
Vale of the Vole
Heaven Cent
Man from Mundania
Isle of View
Question Quest
The Color of Her
Demons Don't Dream
Harpy Thyme
Geis of the Gargoyle
Roc and a Hard Place
Yon Ill Wind
Faun and Games
Zombie Lover
Xone of Contention
The Dastard
Swell Foop
Up in a Heaval
Cube Route
Currant Events
Pet Peeve
Stork Naked
Air Apparent
Two to the Fifth
Jumper Cable
Knot Gneiss
Well-Tempered Clavicle
Luck of the Draw
Esrever Doom
Board Stiff
Five Portraits
Isis Orb
Ghost Writer in the Sky
No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
Lady Aleena
Re: How to get 0 to initialize a value
by AnomalousMonk (Archbishop) on Apr 30, 2019 at 04:31 UTC
|
Others have pointed out that the OPed code seems to work just fine once the
my $first_novel = $novel_list->[$first_book];
statement is fixed to be
my $first_novel = $novel_list[$first_book];
Somewhat off-topic, may I suggest that a better approach might be to use a hash:
Output:
c:\@Work\Perl\monks\Lady_Aleena>perl novel_character_list_2.pl
"A Spell for Chameleon" is the first novel 'Bink' was in.
[Other source] is the first novel 'Quan' was in.
Give a man a fish: <%-{-{-{-<
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I found the problem in my data. I made the assumption my data was correct. I should know better. Sorry for taking up your time.
I took those values OUT of the hash they were in. I wasn't about to post a hash that has 2,198 keys and each of those keys has at least 8 keys. I simplified my code for this problem. And yes, I know I made a mistake with the @novel_list, but the warning still remains. The actual code is...
for my $key (keys %$characters) {
my $character = $characters->{$key};
my $name = $character->{Name};
# about 30 more lines of code to munge the data
my @novels = split(/, /, $character->{book}); # line 132 in code
my $first_book = $novels[0];
my $first_type = $first_book =~ /^M/ ? 'major' : $first_book =~ /^m/
+ ? 'mentioned' : undef; # line 134 in code
$first_book =~ s/\D//g; # line 135 in code
$character->{intro}->{book} = $book_list[$first_book]; # line 136 in
+ code
$character->{intro}->{type} = $first_type;
$character->{book} = \@novels;
# about 30 more lines of code to munge the data
}
The hash would be (I think)...
my $characters = {
Bink => {
Name => 'Bink',
book => 'M1, M2, m3, m4, 6, m7, M9, m11, m13, 14, 17, m19, m21, M2
+2, m23, m31, 35, 36, m37, m40, m41',
},
Quan => {
Name => 'Quan',
book => '0',
},
};
I just didn't feel like making the hash for it. $character->{book} is still coming back uninitialized for Quan.
The errors are...
character_tests.pl: Use of uninitialized value in split at character_t
+ests.pl line 132
character_tests.pl: Use of uninitialized value $first_book in pattern
+match (m//) at character_tests.pl line 134
character_tests.pl: Use of uninitialized value $first_book in pattern
+match (m//) at character_tests.pl line 134
character_tests.pl: Use of uninitialized value $first_book in substitu
+tion (s///) at character_tests.pl line 135
character_tests.pl: Use of uninitialized value $first_book in array el
+ement at character_tests.pl line 136
No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
Lady Aleena
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
use strict;
use warnings;
use Test::More tests => 1;
use Test::NoWarnings;
my $characters = {
Bink => {
Name => 'Bink',
book =>
'M1, M2, m3, m4, 6, m7, M9, m11, m13, 14, 17, m19, m21, M22,
+ m23, m31, 35, 36, m37, m40, m41',
},
Quan => {
Name => 'Quan',
book => '0',
},
};
my @book_list;
for my $key (keys %$characters) {
my $character = $characters->{$key};
my $name = $character->{Name};
# about 30 more lines of code to munge the data
my @novels = split(/, /, $character->{book}); # line 132 in code
my $first_book = $novels[0];
my $first_type = $first_book =~ /^M/ ? 'major' : $first_book =~ /^m/
+ ? 'mentioned' : undef; # line 134 in code
$first_book =~ s/\D//g; # line 135 in code
$character->{intro}->{book} = $book_list[$first_book]; # line 136 in
+ code
$character->{intro}->{type} = $first_type;
$character->{book} = \@novels;
# about 30 more lines of code to munge the data
}
| [reply] [Watch: Dir/Any] [d/l] |
Re: How to get 0 to initialize a value
by tangent (Parson) on Apr 30, 2019 at 02:38 UTC
|
When I run your code I do not get the warnings you mention, but this line:
my $first_novel = $novel_list->[$first_book];
does give a warning fatal error (praise be to monk of anomaly). @novel_list is a plain array not an array reference - it should be:
my $first_novel = $novel_list[$first_book];
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
... this line:
my $first_novel = $novel_list->[$first_book];
does give a warning.
It does not give a warning, it fails to compile due to a fatal error from a stricture that was (wisely!) enabled (use strict; statement at start of code; see strict). You may say this is a trivial difference, but a warning is not fatal — unless you explicitly make it so!
Give a man a fish: <%-{-{-{-<
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to get 0 to initialize a value
by holli (Abbot) on Apr 30, 2019 at 02:36 UTC
|
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to get 0 to initialize a value
by Marshall (Canon) on Apr 30, 2019 at 21:36 UTC
|
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';
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
# Name format... # Salutation...
my $salute = $name eq $EMPTY_STR ? 'Dear Custome
+r'
: $name =~ m/ \A((?:Sir|Dame) \s+ \S+) /xms ? "Dear $1"
: $name =~ m/ (.*), \s+ Ph[.]?D \z /xms ? "Dear Dr $1"
: "Dear $name"
+ ;
The cake is a lie.
The cake is a lie.
The cake is a lie.
| [reply] [Watch: Dir/Any] [d/l] |
|
Thank you for your very thought out responce, Marshall. I would love to use Perl 5.10; however, my ISP uses CPanel, and the CPanel version they have uses Perl 5.8.8 STILL. To say I am frustrated is putting it mildly. I am writing Dark Age Perl. So, I do things the long way around most of the time when I write Perl, except when I am writing Perl just for myself, then I can use 5.20.2 because that is what Debian Jessie comes with.
Bad data, and bad me for not double checking, is to blame for my problem. I fixed it now.
Again thank you for your time and effort.
No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
Lady Aleena
| [reply] [Watch: Dir/Any] |
|
Hi Lady_Aleena!
Thank you for your kind words. I was trying to help.
As far as Perl versions goes, it is possible to install your own version of Perl even if the system default version is 5.8.
I do believe that a system Perl 5.8 is still common. Perl has become part of the unix "biosphere" and is assumed to be there for the O/S to do its job. I can understand why coding to 5.8 makes sense for programs needed by the O/S. Tackling the problem of "user versions of Perl" requires another thread.
Perl 5 is like an evolving biological organism. It morphs and adapts. However, not every new gizmo that shows up
in the latest release of Perl version is worthy of use in production code. I played with the "smart match" operator
and decided that it was too smart for me - I couldn't be sure exactly what it was going to do. Maybe the ambiguities
of that operator have been worked out or not? In any event, I can write a lot of code without needing it.
Some new Perl features like the "//" operator make a lot of sense because it is so clearly different than the
"||" operator. I find some features like: say "xyz"; to be goofy and completely unnecessary. Perl is not Python.
Hang in there, Perl is a great language.
| [reply] [Watch: Dir/Any] [d/l] |
|
|