perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I seem to be forgetting basic symbolic reference usage. I don't understand why the following attempt at using a symbolic reference for an array name doesn't produce correct output. I'm trying to pass array names of interest to a subroutine that prints the subname and it's length. Seems simple enough. But no luck...
#!/usr/bin/perl -w use strict; my ($Devel, $Debuff_Output) = (1,1); if ($Debuff_Output) { select STDERR; $| = 1; select STDOUT; $| = 1; } if ($Devel) { use Carp qw(cluck confess); $SIG{__WARN__} = $SIG{__DIE__} = sub {confess @_} } my @array = (1, 2, 3, 4); my @array2 = (3, 5, 7); sub arlen ($) { my $ar_nam=$_[0]; no strict 'refs'; my $ar_len = eval $#$ar_nam; sprintf "name=%s, length=%d", $ar_nam, $ar_len; } foreach ('array', 'array2') { no strict 'refs'; printf "Array %s\n", arlen $_; }

Replies are listed 'Best First'.
Re: simple symbolic reference Q
by chromatic (Archbishop) on Oct 04, 2010 at 20:54 UTC

    You can't (easily) use symbolic references with lexical variables. You must use package variables instead.

    With that said, don't use symbolic references for this: use a hash or real references.

    (As well, $#whatever doesn't evaluate to the number of elements of @whatever in scalar context; it evaluates to the index of the final element of @whatever. Use my $ar_count = @whatever; instead.)

      Ok, so this works, that's how I got confused. My previous usages had been with package vars. So this works for a limited case of the vars being package vars, but I don't see how you would do the same with has or real references, without having to pass both the string name of the array and the really reference to the array. The hash example -- not sure what you are referring to, specifically.
      #!/usr/bin/perl -w use strict; my ($Devel, $Debuff_Output) = (1,1); if ($Debuff_Output) { select STDERR; $| = 1; select STDOUT; $| = 1; } if ($Devel) { use Carp qw(cluck confess); $SIG{__WARN__} = $SIG{__DIE__} = sub {confess @_} } our @array = (1, 2, 3, 4); our @array2 = (3, 5, 7); sub arlen ($) { my $ar_nam=$_[0]; no strict 'refs'; my $ar_len = @$ar_nam; sprintf "name=%s, length=%d", $ar_nam, $ar_len; } foreach ('array', 'array2') { no strict 'refs'; printf "Array %s\n", arlen $_; }

        Why do you care about the name of the array? (If you really, really do, use Smart::Comments which works just fine with lexicals.)

        (Why are you messing with the buffering of STDERR? Did you know that your code always loads Carp?)

        The hash example -- not sure what you are referring to, specifically.

        chromatic probably meant something like:

        c:\@Work\Perl\monks\AnomalousMonk>perl -wMstrict -le "my %hash = ( foo => [1, 2, 3, 4], bar => [3, 5, 7], ); ; elements(\%hash, qw(bar foo)); ; sub elements { my $hashref = shift; for my $name (@_) { print qq{elements of $name: }, scalar @{ $hashref->{$name} }; } } " elements of bar: 3 elements of foo: 4
Re: simple symbolic reference Q
by morgon (Priest) on Oct 04, 2010 at 22:15 UTC
    I seem to be forgetting basic symbolic reference usage.
    That's good. You should not have learnt it in the first place.
      Um...I just wrote twisted routine to handle a bunch of symbolic references. This is a code snippet where I pass in the name of a variable, and use it to construct 3 different symbolic variable names that seem to all work (the variables are 'package' variables declared using 'our'. How is this different?
      AH... just verified: Our vs. my.
      sub _common_setvar { my $XX = shift; # XX=(db|cache) no strict 'refs'; my $XX_prefix = $XX . '_prefix'; debug("path_ops","_XX_prefix=%s",$XX_prefix); if (@_) { $$XX_prefix = $_[0]; my $_XX_prefix_set = "_" . $XX_prefix . "_set"; debug("path_ops","_XX_prefix_set=%s",$_XX_prefix_set); $$_XX_prefix_set = True; my $_save_XX_prefix = '_save' . $$XX_prefix; if ($_storage_dir_set) { my $tmp = $$_save_XX_prefix = _path_cat($storage_dir,$$XX_prefix); debugn("path_ops", "cmn_sv: sd=%s, ddXX_p=%s, ddsave_XX_p=%s, ", $storage_dir, $tmp, $$XX_prefix); _dir_check($$_save_XX_prefix); } } $$XX_prefix; } sub db_prefix { my $this=shift; _common_setvar('db',@_); debug("path_ops","db=%s, sdb=%s",$db_prefix,$_save_db_prefix); } sub cache_prefix { my $this=shift; _common_setvar('cache',@_); debug("path_ops","cache=%s, scache=%s",$cache_prefix,$_save_cache_ +prefix); }

        Nasty.

        You should have simply used a hash. Drop all the toxic $$XX stuff out. Use simple keys instead of complex variable names with symbolic refs on top.

        $cache{'storage directory'}; for example.

        Using hashes would prune your tree of subroutines, prevent them from having action at a distance, make it clear what variables are changing, and greatly improve your code readability.

Re: simple symbolic reference Q
by eyepopslikeamosquito (Archbishop) on Oct 05, 2010 at 10:59 UTC

    As for why symbolic references should be avoided, see the classic three part series by MJD: part 1 and part 2 and part 3.