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

Ratazong has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

I'm struggeling with packages and eval ... here is my short example:

package items; use strict; use warnings; use Data::Dumper; use Exporter; my %items; 1; sub initItems { $items{1}{Name} = "Rusty Sword"; # fine $items{2}{Value} = 2; # fine eval ("$items{3}{Value} = 2;"); # ERROR: Use of uniniti +alized value in concatenation (.) or string at items.pm line 16. (th +is line) print( Data::Dumper->Dumpxs( [ \ %items ], [ qw{ *items } ] )); }
When running it (calling items::initItems(); in my main program), I get the error mentioned above, and the output is:
%items = ( '1' => { 'Name' => 'Rusty Sword' }, '3' => {}, '2' => { 'Value' => 2 } );
What is wrong? Why does the eval somehow access my hash, but does not add the value? Is it something obvious and I'm just blind today?

Thanks for help! Rata

Replies are listed 'Best First'.
Re: Eval/package question
by GrandFather (Saint) on Jan 21, 2020 at 10:33 UTC

    We can simplify the test case to:

    use strict; use warnings; my %items; eval ("$items{3}{Value} = 2;");

    The issue is that $items{3}{Value} is evaluated as the string is being constructed. A simple fix it to use single quotes so interpolation doesn't happen. A better fix is not to use string eval!

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Eval/package question
by Eily (Monsignor) on Jan 21, 2020 at 10:59 UTC

    You already got the answer from GrandFather, but here's another way to see what's happening:

    print("$items{3}{Value} = 2;"); eval("$items{3}{Value} = 2;");
    That way you'll see exactly what eval receives as an argument.

    Why are you using eval in the first place? String eval means the code isn't compiled until the very last moment, so if you introduce some mistake like a syntax error, you wouldn't get notified until you execute the bit of code that contains the eval (which might not be everytime). If you're trying to catch errors, using a block eval (eval { $value =  2; }) lets perl compile the code rightaway, but only execute the section separately

    Edit: corrected my code tags thanks to choroba's sharp vision :P

      Thanks a lot, GrandFather and Eily!

      I have a generic configuration in an Excel-file, and move it to several hashes by creating the commands as strings and eval-ing them. It works in my stand-alone-script, but doesn't any longer when moving the code into the package (the package variable (%items above) is not available). So my example above did not reproduce my issue - and I'll have to search further.

      Nevertheless, I learned something today :-)

      Have a great day, Rata

        I have a generic configuration in an Excel-file, and move it to several hashes by creating the commands as strings and eval-ing them.

        As others have mentioned, stringy eval is often not a good idea. Especially when working with hashes, what's wrong with the following?

        our %items; my $item = '3'; my $key = 'Value'; my $value = 2; $items{$item}{$key} = $value;
        (the package variable (%items above) is not available).

        It is not, because %items is declared as a my variable and thus available only in the the package itself, not in the main package.

        If you have to access it from main, you either have to fully qualify it as %items::items, or provide methods to access it (exporting it, providing accessors or a method which returns a reference to it, etc), but it's hard to say what's best without knowing the whole scenario.

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
        I have a generic configuration in an Excel-file

        Have you considered exporting that as CSV and using Text::CSV to load the data?

        (Hint: Data::Dumper will tell you what you have at intermediate stages if you are unsure.)

Re: Eval/package question
by shmem (Chancellor) on Jan 21, 2020 at 14:26 UTC
    What is wrong? Why does the eval somehow access my hash, but does not add the value? Is it something obvious and I'm just blind today?

    It does access the hash and puts this slots value on the LHS, not the hash slot accessing expression. So yes, you're being blind :-)
    Try

    eval ("\$items{3}{Value} = 2;"); # also: eval ('$items{3}{Value} = 2;');

    Had you checked $@ after the eval, you would have seen that it contains

    syntax error at (eval 2) line 1, near "="

    since the LHS of the assignment is empty.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
A reply falls below the community's threshold of quality. You may see it by logging in.