[Techtalk] Perl Frustration

Almut Behrens almut-behrens at gmx.net
Sat Nov 1 14:21:06 EST 2003


On Fri, Oct 31, 2003 at 04:18:39PM -0800, Kai MacTane wrote:
> > 
> >    ${"main::${thisline}_foo"} = $thisline;
> >  vs.
> >    eval "\$main::${thisline}_foo = '$thisline'";
> 
> Do you have any opinion on which would be more readable and understandable 
> to the average Perl coder?

hard to tell... both incantations probably look like line noise to the
uninitiated ;)

I guess the eval variant is easier to understand conceptually (in the
various Perl courses I've given over the years, I always found that
students quite easily grasp the eval concept...), however, the string
to eval usually gets a little ugly due to quoting issues (though, for
debugging purposes, you can always substitute "eval" with "print" to
see what's going on...).

The other variant probably is not so easy to comprehend, if you haven't
yet heard of symbolic references (or symbol table hashes, typeglobs and
stuff...). Personally, I find this version slightly more concise, Also,
there are no quoting issues on the right hand side -- you can assign
everything as usual. On the other hand, this syntax is kind of
'deprecated' wrt writing readable code, or at least seen as potentially
dangerous by strict checking. So, as always, YMMV ;)


To spin this a little further, I should admit that I forgot to mention
a third variant in my previous post:

  chomp($thisline = <STDIN>);
  while ($thisline =~/\w/) {
      $main::{"${thisline}_foo"} = \"$thisline";
      chomp($thisline = <STDIN>);
  }

This construct has the advantage of passing strict checking, so you
don't need to fiddle with "no strict ...".
To really understand what's being done here, you need to take a closer
look -- I guess I should elaborate:

Perl has the concept of typeglobs, specified as *varname, which stands
for all of $varname, @varname, %varname, etc. You can assign them whole
other typeglobs, or selectively fill individual slots in the typeglob
by assigning the appropriate type of reference, e.g.

  *varname = \$scalar;
  *varname = \@array;
  *varname = \@hash;

Perl's internal machinery is intelligent enough to put the reference in
the right slot depending on its type, so if you write

  *varname = [ "some value" ];

you could, for example, access the first element of the array as

  print $varname[0];

which would print "some val".

Assuming that varname belongs to the package main::, you could
equivalently write

  $main::{varname} = <some reference>;

This is nothing more than a direct assignment into the package's symbol
table hash (often called 'stash'), and thus is not subject to strict
checking (Perl's inventors in this case obviously assumed you know what
you're doing...).

As with any hash, the keys (=variable names) can be constructed
dynamically, so in your case you would write

  $main::{"${thisline}_foo"} = ...
or
  $main::{$thisline.'_foo'} = ...

The main difference compared to the symbolic refs syntax (mentioned at
the top) is that you now have to put a reference on the right hand
side, a scalar ref in your case. This brings up another minor issue.
While you can easily construct anonymous refs to arrays or hashes,

  $main::{varname} = [ ... ];   # defines @varname
  $main::{varname} = { ... };   # defines %varname
  
what would be the comparable syntax for scalars? Well, you could always
write

  my $tmp = "some value";
  $main::{varname} = \$tmp;

or, in your specific case

  chomp($thisline = <STDIN>);
  while ($thisline =~/\w/) {
      my $tmp = $thisline;
      $main::{"${thisline}_foo"} = \$tmp;
      chomp($thisline = <STDIN>);
  }

Note that this is _not_ the same as directly writing

      ...
      $main::{"${thisline}_foo"} = \$thisline;
      ...

Using the additional variable $tmp essentially sets up a 'closure'
situation: the reference count of $tmp doesn't drop to zero when $tmp
goes out of scope, because the symbol table is still holding a
reference to the memory internally allocated for $tmp. So, the value
you assigned to $tmp will be retained, and still be accessible via
$main::varname. If you directly assigned a reference to $thisline, the
value would be overwritten each time, as $thisline is declared in the
outer scope (not within the while block).

However, it wouldn't be Perl if you couldn't get rid of the
intermediate variable ;)

  ...
  $main::{"${thisline}_foo"} = \"$thisline";
  ...

which creates a new scalar (string) variable, getting a copy of
$thisline's value. The operator \ then as usual returns a reference to
it (the copy), that you can assign.

Readability? Well ...

Almut


More information about the Techtalk mailing list