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

I ran into a minor snag using Data::Alias -- I'm not sure if it's a bug, or a limitation that won't be easily fixable ...
>perl -e ' use Data::Alias; my @local_vars; $#local_vars=5; alias my ($a, $b, $c) = @local_vars; $b=1; ' Modification of a read-only value attempted at -e line 5.
Why isn't it auto-vivifying the storage? A 'poor' workaround is to manually assign 'undef' to each member in local_vars, i.e. This works:
> perl -e ' use Data::Alias; my @local_vars=(undef,undef,undef); alias my ($a, $b, $c) = @local_vars; $b=1; '
--- The above is more a question of 'why', as in trying to move forward, I found a _SLIGHTLY_ more reasonable workaround, but still not ideal...
#!/usr/bin/perl -w package mypackage; use Data::Alias; our $local_varnames = 'qw( $a $b $c )'; our $len_local_varnames; { # prefer-> $len_local_varnames= wantarray (eval"($local_varnames)"); +OR # $len_local_varnames= 0+eval"($local_varnames)"; $len_local_varname = eval "local @_=($local_varnames)"; } printf "len_local_varnames=%d\n",$len_local_varnames;
The above works -- though I'd prefer to just take the length of the newly instantiated array enclosed by parens rather than having to do an assignment, which will (I believe?) create a run-time execution charge for the assignment. While it's not a big deal in the 'setup' at the top of the package, in the subroutines where it is used, that means I have to do the same assignment, each time I want to use Data::Alias.

I.e. This doesn't work:

#!/usr/bin/perl -w package mypackage; use Data::Alias; our $local_varnames = 'qw( $a $b $c )'; our $len_local_varnames; { $len_local_varnames= eval "local @_=($local_varnames)" } sub mysub { my $this=shift; my @localspace; $#localspace = $len_local_varnames-1; # <-doesn't work to pre-all +ocate array $this->{localvars}= \@localspace; alias my ($a, $b, $c) = @localspace; ($a, $b, $c) = (1, 2, 3); # (fails because no space a +lloc'ed to localspace) #both print wrong/bad values printf "lsp[1]=%d\n",$localspace[1]; printf "b=%d\n",$this->{localvars}->[1]; } &mysub;
To make the above work, I throw out the "$#localspace = $len_local_varnames-1;" line, and change the one above it to:
my @localspace = eval "local @_=($local_varnames)";
...thus requiring my assignment to be done in each package sub where I want to access my local variables; somewhat inefficient to say the least. The next problem I have is related, but I feel I really need to split it, or this post/question will get really 'unclear', so please forgive me for splitting apart a related problem, but when I tried putting it all in one post, it got very confusing (and my writing is often confusing enough for other people, when I think it is clear -- goddess forbid I post something with hope for understanding that _I_ think is confusing...

So for this question -- the real question is how to get the array to "pre-allocate" without assigning to each member. I thought assigning to the last index would do it, but apparently not. Most frustrating. Would this be considered a bug in 'Data::Alias'?, Or is it likely, due to some perl internals, that they aren't going to be able to fix it without themselves, actually initializing each element (which might look cleaner, but would still be as inefficient, I think...

Sorry for the arcane questions...but if it was easy, I wouldn't need the wisdom of perlmonks! ;-)

  • Comment on Error using Data::Alias (how to allocate array w/o filling it in?) and help avoiding 'constant' len?
  • Select or Download Code

Replies are listed 'Best First'.
Re: Error using Data::Alias (how to allocate array w/o filling it in?) and help avoiding 'constant' len?
by ikegami (Patriarch) on Sep 22, 2010 at 22:30 UTC

    The above is more a question of 'why'

    $a, $b and $c are being aliased to The undef (PL_sv_undef) rather than an undef (a scalar that happens to be undefined).

    $ perl -MDevel::Peek -e'Dump(undef); Dump(my $x);' SV = NULL(0x0) at 0x8168784 REFCNT = 2147483616 FLAGS = (READONLY) SV = NULL(0x0) at 0x817bc18 REFCNT = 1 FLAGS = (PADMY)

    It uses less memory to initially map elements to an existing scalar than to create a bunch of undefined scalars.

    The solution is to reverse the mapping.

    alias my @array = my ($x, $y, $z);

    If that syntax doesn't work ( Tested: It does work ), you can achieve the intended as follows:

    sub map_scalars_onto_array(\@@) { my $array = shift; alias $array->[$_] = $_[0] for 0..$#_; } map_scalars_onto_array my @local_array, my ($x, $y, $z);

    By the way, you can force the vivification of an array element by taking a reference to it.

    $ perl -E'$#a=5; for (1..2) { say exists($a[3])?1:0; \$a[3]; }' 0 1
      Doing the reverse mapping seems like the best...I like it. Quirky, but I like it, and it makes sense too, since the values on the right are enumerated -- forcing them into 'undef'ed existence,

      I was trying to avoid anything that looped over the array elements, since that felt like wasted work in this case. I'll have to see if that solves my other issue, which was determining the length of my local variable list, putting their names into a list, and using that name-list in multiple places. I meant to put that in as a separate question, but got side tracked...but now will first see if any of the information you present here will solve issues I had in that problem.

      Thanks! It's excellent to have another set of eyes look over something like this when a simple solution can be staring one in the face!...

        I was trying to avoid anything that looped over the array elements

        Impossible. You want to do N aliases, so something will have to loop over the N variables/elements.

      Actually turning it around helped me avoid the alias altogether -- save the vars on the 'stack', and not have to worry about early exists while having the saved values still being updated on the stack... Got rid of 'local_vars' array entirely, and now I just push them all on at creation time onto the stack:
      push @{this-{local_vars}], [my ($open_tag, $tag_print, $cur_class, $cur_id, $defines_class, $defines_id, $tag_output )];
      Came close to forgetting the square brackets until I was about to explain what I did here! Then I saw,, oops, I'm pushing 7 successive values onto @local_vars, instead of pushing 1 array containing them! Doh! But try to explain what I did, and caught that right off!

      Good illustration of why sometimes I snag some non-CS literate friend into helping look for some programing problem while I explain it to them in a way they can understand it -- meaning I really have to be very explicit as to what is going on -- maybe even with diagrams. Embarrassingly amazing how many times I catch my own bugs that way.

      I try to explain to them, that they really are performing a valuable function -- even if they don't understand everything, because when you explain your code so a non-CS person can understand it, you have to explain every assumption, and the effect of every operator...

        huh?

        push @{$this->{local_vars}}, [my ($open_tag, $tag_print, $cur_class, $cur_id, $defines_class, $defines_id, $tag_output )]; $open_tag = 123; use Data::Dumper; print(Dumper($this));
        $VAR1 = { 'local_vars' => [ [ undef, undef, undef, undef, undef, undef, undef ] ] };

        Mind you, putting the assignment to local_vars at the bottom will avoid aliases. That's the solution I suggested in your earlier thread.

        my ($open_tag, $tag_print, $cur_class, $cur_id, $defines_class, $defines_id, $tag_output ); $open_tag = 123; push @{$this->{local_vars}}, [$open_tag, $tag_print, $cur_class, $cur_id, $defines_class, $defines_id, $tag_output];
        $VAR1 = { 'local_vars' => [ [ 123, undef, undef, undef, undef, undef, undef ] ] };