Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

How to get 0 to initialize a value

by Lady_Aleena (Priest)
on Apr 30, 2019 at 00:27 UTC ( [id://1233163]=perlquestion: print w/replies, xml ) Need Help??

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

Replies are listed 'Best First'.
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:  <%-{-{-{-<

      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

        SSCCE:

        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 }
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];
      ... 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:  <%-{-{-{-<

Re: How to get 0 to initialize a value
by holli (Abbot) on Apr 30, 2019 at 02:36 UTC
    Don't put a use strict; in you code example when in fact you're not using it. If you did, then you'd have noticed $novel_list is not defined anywhere. And no, "0" does not mean undefined or will trigger an undefined warning ever.


    holli

    You can lead your users to water, but alas, you cannot drown them.
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';

      Perl Best Practices (chapter 6; don't have dead tree version handy so no clue on a page number) recommends laying chained ternary expressions out as a table. If you squint it almost looks haskelly.

      # 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.

      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
        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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1233163]
Approved by graff
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2024-03-28 23:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found