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

Respected Monks,

I am trying an example from Beginning Perl (Curtis Ovid Poe) and in Chapter 5, there's a script about extracting unique values. I'm trying to understanding it, but spectacularly failing. Say I have something like this:

my @array = (1,2,3,4,1,1,3,3,45,54,55); my %unique; @unique{@array} = undef; foreach my $key (sort keys %unique) { print "$key\n"; }

It works as expected, but I don't know why it works. So I checked what perldoc has to say about undef and that still didn't clear it out for me.

I went to the stupid extent of changing @unique{@array} to @something{@array} and the warning made me realize that @unique has to be there because it denotes that it's a slice of the %unique array. But what is it that the undef do to the slice that it doesn't make it throw errors. How does it work? If I remove the undef keyword, I get a warning stating "useless use of hash slice in a void context. I'm just not getting it.

Note: I went through hash slice and there's a paragraph in there that states:

"What happens when you undef a slice is that it is converted into a list, which is then evaluated in scalar context, which yields the last element of the slice as the scalar value. That value (and only that value) is then set to undef (so it's not a no-op; if you want a no-op use the \ operator, which will take a list). Contrast that with handing it an array proper. The entire array is then undefined. The conversion from hash slice to list to scalar is not documented, and should generate a warning. In fact, if you try to pass undef a list of array elements, you will get an error."

I still didn't clearly get it. :(

Replies are listed 'Best First'.
Re: What does undef do when I say @hashslice{@array} = undef
by Corion (Patriarch) on Nov 05, 2014 at 09:58 UTC

    @foo{...} (note the curly braces) is Perl syntax for a "hash slice". The undef @unique{ @array } sets each key of %unique that is in @array to undef. This is a common and highly efficient way to create a list of keys in a hash, if you don't care about the value. As the goal is to make the list in @array unique, using the keys is the common approach as keys are always unique.

    You can inspect what happens to %unique by using Data::Dumper:

    use Data::Dumper; print "Before: " . Dumper \%unique; @unique{ @array }= undef; print "After: " . Dumper \%unique;

      Hi Corion,

      You sir are a Genius!! I see it!!

      C:\Users\pritesh\textfiles>perl undef.pl Before: $VAR1 = {}; After: $VAR1 = { '1' => undef, '3' => undef, '54' => undef, '2' => undef, '4' => undef, '45' => undef, '55' => undef };

      So like you said, the undef @unique{@array} basically does the magical thing of taking all the the elements in the @array, setting them as a key and "undef" the value. Now, keys gotta be unique, so it's going to remove the duplicates, and just keep the unique keys.

      Boy, I feel like Neo when he finally "gets" the Matrix :D

      On a serious note, Data::Dumper should be called Data::Enlightenment :)

        Hello pritesh,

        the undef @unique{@array} basically does the magical thing of taking all the the elements in the @array, setting them as a key and "undef" the value.

        Yes, and in this case the magic is performed by an important Perl feature called autovivification, which Wikipedia explains as follows:

        In the Perl programming language, autovivification is the automatic creation of new arrays and hashes as required every time an undefined value is dereferenced. Perl autovivification allows a programmer to refer to a structured variable, and arbitrary sub-elements of that structured variable, without expressly declaring the existence of the variable and its complete structure beforehand.

        Here are some references:

        If you say:

        @unique{ @array } = 'X';

        what actually happens is that the first element of the hash slice is set to 'X', and all the remaining elements in the hash slice are set to undef (because 'X' is treated as though it were ('X'), i.e., a list with one element; that element is assigned to the first element on the left-hand side of the assignment, and, since there are no more elements in the right-hand-side list, undef is assigned to the remaining elements on the left-hand side.) The values corresponding to the keys already present in %unique but not in @array remain unaffected. But the values corresponding to keys present in both %unique and @array are overwritten.

        Now, if any elements in @array are not already present as keys in %unique, the assignment turns these elements of the hash slice into lvalues. (An lvalue is a value that can be assigned-to, that is, that can appear on the left-hand side of an assignment.) And that’s where autovivification comes in: if these lvalues don’t already exist, Perl creates them for you.

        Note that the syntax:

        undef @unique{ @array };

        produces a similar result, but there are two differences:

        • It is the last element of the hash slice which is explicitly set to undef.
        • If an element in @array is already present as a key in %unique, the corresponding value is not overwritten (unless that element happens to be the last in @array, in which case it is set to undef).

        But again, it is autovivification that is responsible for the magic that makes this a useful idiom.

        Hope that helps,

        Update: Improved wording.

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: What does undef do when I say @hashslice{@array} = undef
by Anonymous Monk on Nov 05, 2014 at 09:52 UTC