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

Hi, monks of Perl!

I have a number of variables where I need the name for logging. The names are known a priori, and a nominal amount of munging needs to occur within a function.

Pseudo-code for what needs to happen is as follows:

#!/bin/env perl use strict; use warnings; my ($a, $b, $c); $a = $b = $c = 0; foreach my $v (qw/a b c/) { func($v); } sub func { my $v = shift; print "v = $v\n"; eval($v = $v * 2); # incorrect syntax }

I know the eval() statement above is wrong, but I can't get either the expression or block form working. I'm rather confused.

Any enlightenment you can pass on would be greatly appreciated!

Replies are listed 'Best First'.
Re: eval & variable names?
by ikegami (Patriarch) on Jan 21, 2012 at 00:27 UTC
    When $v contains "a", you want to execute
    $a = $a * 2

    Which you means you have to build that string and pass it to the parser (eval EXPR). That can be done using the string literal

    "\$$v = \$$v * 2"

    So you want

    eval "\$$v = \$$v * 2; 1" or die $@;

      Thanks!

      I see that I need to look at the string being passed to eval().

      Thanks, again for slapping me back to reality. I guess it's getting late...

Re: eval & variable names?
by CountZero (Bishop) on Jan 21, 2012 at 07:54 UTC
    No need to use eval or even to know the variable names a priori!

    The values you pass into a subroutine are aliased, in other words, the elements of @_ are actually the variables you passed in and whatever you do to the elements of @_ happens to the original variables.

    Consider:

    use Modern::Perl; use PadWalker qw/peek_my peek_our/; my ($first,$second, $third) = qw /1 2 3/; double_me($first,$second, $third); say "$first $second $third"; sub double_me { say get_name(1, $_) for @_; $_*=2 for @_; } sub get_name_my { my $pad = peek_my($_[0] + 1); for (keys %$pad) { return $_ if $$pad{$_} == \$_[1] } } sub get_name_our { my $pad = peek_our($_[0] + 1); for (keys %$pad) { return $_ if $$pad{$_} == \$_[1] } } sub get_name_stash { my $caller = caller($_[0]) . '::'; my $stash = do { no strict 'refs'; \%$caller }; my %lookup; for my $name (keys %$stash) { if (ref \$$stash{$name} eq 'GLOB') { for (['$' => 'SCALAR'], ['@' => 'ARRAY'], ['%' => 'HASH'], ['&' => 'CODE']) { if (my $ref = *{$$stash{$name}}{$$_[1]}) { $lookup{$ref} ||= $$_[0] . $caller . $name } } } } $lookup{\$_[1]} } sub get_name { unshift @_, @_ == 2 ? 1 + shift : 1; &get_name_my or &get_name_our or &get_name_stash }
    Output:
    $first $second $third 2 4 6

    1. Because of the aliasing it is considered a "Perl Best Practice" to immediately assign @_ to lexical variables in the subroutine, so as not to have any inadvertent effect "at a distance" on the original variables. Of course this is exactly what you want to happen here, so go ahead!
    2. I have found the solution to get your variable names on StackOverFlow

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: eval & variable names?
by educated_foo (Vicar) on Jan 21, 2012 at 09:07 UTC
    You probably want to do this:
    #!/bin/env perl use strict; use warnings; my ($a, $b, $c); $a = $b = $c = 0; foreach my $v (qw/a b c/) { func($v); } sub func { my $v = shift; print "v = $v\n"; eval '$'.$v.' = $'.$v.' * 2'; } print "$a $b $c\n";
    It's completely hideous, but it works as long as func() isn't defined in another file.
Re: eval & variable names?
by JavaFan (Canon) on Jan 21, 2012 at 11:25 UTC
    If you want symbolic references, just use them. Just because "use strict" prevents you from using them by accident doesn't mean it's bad to use them. Untested code:
    our ($a, $b, $c) = (0, 0, 0); foreach my $v (qw/a b c/) { func($v); } sub func { my $v = shift; print "v = $v\n"; no strict 'refs'; $$v *= 2; }
Re: eval & variable names?
by ricDeez (Scribe) on Jan 21, 2012 at 01:33 UTC

    Slightly off topic but you can also use an anonymous function rather than the eval. This is what I quickly came up with:

    #!/bin/env perl use strict; use warnings; use 5.012; my ($a, $b, $c); $a = $b = $c = 0; foreach my $v (qw/a b c/) { func($v); } sub func { my $in = shift; say "passed in: $in"; print "returned: "; return sub { my $in = shift; say $in x 2; }->($in); }

      Actually, disregard that as it still won't work with $a, $b, $c rather than a, b, c... Humble apologies, I misread your question!