Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling

nested variable resolution

by mpettis (Beadle)
on Jul 17, 2007 at 21:45 UTC ( #627132=perlquestion: print w/replies, xml ) Need Help??
mpettis has asked for the wisdom of the Perl Monks concerning the following question:

Hi, Can someone help me figure out how to do nested variable resolves and evaluation of numeric operations? I am trying to make a system that stores formulas and variable values in an external file, and i want my perl script to pull in the values and the formulas, and then evaluate the formula with the variables having their values substituted into the formula. A small, representative, and problematic piece of code follows:
my $x = 3; my $y = "2 * $x"; my $z = eval {$y}; print '$z is: ', $z, "\n"; print 'eval of $z is: ', eval{$z}, "\n"; print 'eval of "2 * 3" is: ', eval '2 * 3', "\n";
What I want is for the 2nd print statement to evaluate to the value '6', which the 3rd print statement does. Below is what I get:
$z is: 2 * 3
eval of $z is: 2 * 3
eval of "2 * 3" is: 6
Again, can someone help me figure out how to do nested variable resolves and evaluation of numeric operations? Thanks, Matt

Replies are listed 'Best First'.
Re: nested variable resolution
by Corion (Pope) on Jul 17, 2007 at 21:51 UTC

    There is a vast difference between eval with a string and eval with a block. String-eval will evaluate the string, while block eval will execute the block and catch any exceptions raised in there. What you (seem to) want is the string form of eval:

    use strict; my $x = 3; my $y = "2 * $x"; # interpolation here sets y to '2 * 3' already! my $z = eval $y; # $z is 6 now print "\$x is $x"; print "\$y is $y"; print "\$z is $z";

    More likely though, in the long run, you will want to ditch eval and build your own parser for mathematical expressions. There are two or three approaches to such parsers, the best documented way is the way Dominus describes in his book Higher Order Perl. The second approach would be to use Parse::RecDescent, which allows a "natural" approach to parsing but has horribly bad error messages whenever you mess up your input. The third approach is to use Parse::YAPP, which is mostly like the yacc compiler builder, but for Perl. I found yacc (and P:YAPP) to be unapproachable first, but the speed made up for the steeper learning curve.

Re: nested variable resolution
by FunkyMonk (Chancellor) on Jul 17, 2007 at 21:50 UTC
    You're so nearly there!

    Change your block eval (eval{$z}) to a string eval (eval $z)

    my $x = 3; my $y = "2 * $x"; my $z = eval {$y}; print '$z is: ', $z, "\n"; print 'eval of $z is: ', eval $z, "\n";

    update: useless-use-of-quotes-noob-error eliminated. Yes, I should know better:(

      It worked, thanks! If you have a moment, can you tell me what the difference between using {} and "" is? I'll look it up in perlfunc as well, but if you have any insight. Thanks a ton! Matt
        The 'eval EXPR' version evaluates code at runtime like you expect while the 'eval BLOCK' version is used for exception handling (and is not evaluated at runtime).

        For example normally DBI errors are fatal to the entire program. However, if I wrap my DBI calls in an eval BLOCK structure I can then catch a fatal DBI error and take steps to deal with that in my program such as logging it, restarting the database connection, etc. The important thing is that the fatal error doesn't take my script with it.

        Personally I think using the same name is confusing. I would have preferred something like 'catch BLOCK' instead (or perhaps 'exception BLOCK').

        Here's some 'eval BLOCK' code I use:

        sub cmd_wrapper { my( $cmd ) = @_; my( $rc , $out ); eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm 15; $out = (`$cmd`)[0]; # Get first line of output $rc = $?; alarm 0; }; if( $@ ) { die "Eval failed for $cmd but not because of alarm" if $@ ne " +alarm\n"; # Propogate unexpected errors die "Eval failed for $cmd because alarm timed out"; } die "Return code undefined for $cmd" unless defined $rc; return $rc, $out if wantarray; return $rc; }

        See also Corion's post: Re: nested variable resolution.

        eval explains it much better than I can. It's your first port of call if you're having problems with eval.

        Have a look, and if you're still having problems, ask!

Re: nested variable resolution
by Zaxo (Archbishop) on Jul 17, 2007 at 21:51 UTC

    Take a look at Math::Expr. It stores math expressions as a parse tree object. You can store such objects in files with Data::Dumper.

    After Compline,

Re: nested variable resolution
by GrandFather (Sage) on Jul 17, 2007 at 21:54 UTC

    Just use a (generally considered evil) string eval in both cases:

    ... print '$z is: ', $z, "\n"; print 'eval of $z is: ', eval $z, "\n"; print 'eval of "2 * 3" is: ', eval '2 * 3', "\n";


    $z is: 2 * 3 eval of $z is: 6 eval of "2 * 3" is: 6

    DWIM is Perl's answer to Gödel
Re: nested variable resolution
by Limbic~Region (Chancellor) on Jul 18, 2007 at 12:56 UTC
    A mathematical expression evaluator is the problem I tackled in Breaking The Rules II. It probably is overkill for your problem but is worth a look.

    Cheers - L~R

Re: nested variable resolution
by mpettis (Beadle) on Jul 18, 2007 at 03:00 UTC
    Thanks to everyone for your comments -- I have a working solution and a lot to go on now! mpettis
Re: nested variable resolution
by mpettis (Beadle) on Jul 19, 2007 at 17:19 UTC
    Hi All Again,

    I'm not sure whether I should start a new thread or continue this one, so I'll continue this one for now.

    All of the suggestions were great, and I now have my technical problem fixed.

    I have a larger question, though. I'm trying to put together a program where people can specify variable values and formulas to combine those variables in a configuration file. Using the Math::Expr or Math::Expression modules seems like the best way to go about this to avoid reinventing the wheel. I'd just like to know if there is some other module or framework that people have used that they would consider a best practice for evaluating variables and formulas that are user-provided.

    Thanks again for all the help,

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://627132]
Approved by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (13)
As of 2018-06-25 13:01 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (126 votes). Check out past polls.