http://www.perlmonks.org?node_id=739586

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

Dear monks,

I expected the code will print nothing. But, it's not.

$a = 10; @a = qw(1 2 3); *b=*a; undef *a; print @b;

How this prints the output as 1 2 3?

Replies are listed 'Best First'.
Re: Tell me how it works!
by ikegami (Patriarch) on Jan 28, 2009 at 16:14 UTC

    undef(@a) would give you want you want. undef isn't recursive. Undefining the glob doesn't undefine the values referenced by the glob.

    Before glob assignment ---------------------- *main::a *main::b | | | +-------+ +-------+ | +--> | Glob | | Glob | <--+ +-------+ +-------+ +-------+ +-------+ | GP ----> | GP | | GP | <---- GP | +-------+ +-------+ +-------+ +-------+ +-- ARRAY | | ARRAY --+ +-------+ | +-------+ +-------+ | | Array | <-+ +-> NULL +-------+ | 1,2,3 | +-------+ After glob assignment --------------------- *main::a *main::b | | | +-------+ +-------+ | +--> | Glob | | Glob | <--+ +-------+ +--------+ +-------+ | GP ---------> | GP | <--------- GP | +-------+ +--------+ +-------+ | ARRAY --+ +--------+ | +-------+ +-> | Array | +-------+ | 1,2,3 | +-------+ After "undef *a" (Gets a fresh GP, undoing the glob assignment) ----------------- *main::a *main::b | | | +-------+ +-------+ | +--> | Glob | | Glob | <--+ +-------+ +-------+ +-------+ +-------+ | GP ----> | GP | | GP | <---- GP | +-------+ +-------+ +-------+ +-------+ +-- ARRAY | | ARRAY --+ | +-------+ +-------+ | +-------+ NULL <-+ +-> | Array | +-------+ | 1,2,3 | +-------+ If you had done "undef @a" (Clears @a) -------------------------- *main::a *main::b | | | +-------+ +-------+ | +--> | Glob | | Glob | <--+ +-------+ +--------+ +-------+ | GP ---------> | GP | <--------- GP | +-------+ +--------+ +-------+ | ARRAY --+ +--------+ | +-------+ +-> | Array | +-------+ | () | +-------+

    Note that "@a=();" is usually better than "undef @a;" since it doesn't discard @a's buffer.

      Did you create the ascii art yourself? Seems like that would be extremely tedious to do.

      And you didn't even know bears could type.

        Yes to both.
Re: Tell me how it works!
by zentara (Archbishop) on Jan 28, 2009 at 14:58 UTC
    Since no one else mentioned it, as a habit, avoid using $a and $b as variables....they are used in the sort function, and will cause trouble if you sort anywhere in your script.

    I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: Tell me how it works!
by citromatik (Curate) on Jan 28, 2009 at 15:31 UTC

    Because undef *a undefines the typeglob *a, but not the underlying slots. Look at these 2 examples:

    #1 $a = 10; *b = *a; $a = 4; print $b; # <- prints 4 #2 $a = 10; *b = *a; undef *a; $a = 4; print $b; # <- prints 10!

    citromatik

Re: Tell me how it works!
by gone2015 (Deacon) on Jan 28, 2009 at 14:49 UTC

    Because *a is implicitly a collection of pointers to the various $a, @a, etc values. So *b = *a copies those pointers, and undef *a wipes out the original collection of pointers, but not the copy.

    Update: my apologies, I omitted one level of indirection. I am grateful to brother citromatik for pointing out the error of my ways. (I will now go and mediate on the problem of hubris.)

    Correcting my earlier explanation... What is happening is that *a implicitly refers to a structure which contains a collection of pointers to the various $a, @a, etc values. So *b = *a makes *b implicitly refer to the same structure as *a. The operation undef *a wipes out the implicit reference -- it does not affect *b, or the structure, or the values pointed to by that structure.

      So *b = *a copies those pointers, and undef *a wipes out the original collection of pointers, but not the copy.

      That is not true, typeglob assignment performs an aliasing operation:

      $x = 10; @x = qw(1 2 3); *y = *x; $x = 12; print $y; # <- Prints 12 undef $x; print $y; # <- Warns "Use of uninitialized value in..."

      See perlmod

      citromatik

        Thank you, I understood your reply.

        *a=*b;

        But, I read somewhere that assigning the typeglob(*a) will make both to point the same location(address/ memory).

        So, When I undef the *a, how the *b remains the same?

        Can you clear me in this?

Re: Tell me how it works!
by jethro (Monsignor) on Jan 28, 2009 at 14:49 UTC
    From the camel-book:
    Use only on a scalar value, an entire array or hash, or a subroutine n +ame. ... The undef function will probably not do what you expect on most specia +l variables

      Hmmm... That is not what the documentation says (at least for versions 5.10 and 5.8.8)

      undef - Undefines the value of EXPR, which must be an lvalue. Use only on a scalar value, an array (using "@"), a hash (using "%"), a subroutine (using "&"), or a typeglob (using "*")

      citromatik

Re: Tell me how it works!
by Bloodnok (Vicar) on Jan 28, 2009 at 17:14 UTC
    When seeking advice &/or instruction, it's considerably more polite to ask (rather than instruct) e.g. Please tell me how this works, Can anyone explain this ? etc.

    A user level that continues to overstate my experience :-))
Re: Tell me how it works!
by Marshall (Canon) on Jan 28, 2009 at 17:47 UTC
    Very wise Monks have covered the "how". I am curious as to the "why"?. What is the purpose and the real world application?
      Why what? Why create an alias?
      sub inplace_uc { our $text; local *text = \$_[0]; $text =~ tr/a-z/A-Z/; }

      Update: Added missing "$text =~".

        I was enquiring as to what the orginal poster wanted to accomplish and why he thought he needed the typeglob syntax. That's a different question than how it works. You definately know how it works. We have a great intellectual question, but maybe we should be answering a different question? I'm just probing a bit to see if there is some more fundamental application issue here.

        I looked at this inplace_uc code and I did add "$text =~" in front of the tr, which seemed appropriate.

        Here, there is really no performance to be gained that I can see. The inplace_uc() call passes the whole string on the stack and there is some stuff to get the address of it. If we are doing an "inplace" replacement, why not just pass the addresss of the string instead of the whole string? My inplace_uc2() uses the address of the string in a very straightforward way.

        As a perfence of style, I would always expect an inplace modification to to take place on an address or in Perl lingo a referece to something. Ok, mileage varies as they say.

        #!/usr/bin/perl -w use strict; my $lc_msg = 'message'; inplace_uc ($lc_msg); print "$lc_msg\n"; sub inplace_uc { our $text; local *text = \$_[0]; # equivalent to: # local *text = \shift; $text =~ tr/a-z/A-Z/; # added $text =~ } my $lc_msg_y = 'another_message'; inplace_uc2 (\$lc_msg_y); print "$lc_msg_y\n"; sub inplace_uc2 { my $str_ref = shift; $$str_ref =~ tr/a-z/A-Z/; } # prints: #MESSAGE #ANOTHER_MESSAGE