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

I'm not able to understand a code. Mainly the if( not $seen{$element}++ ) looping and what exactly does this statement mean.

#!/usr/bin/perl use strict; use warnings; use diagnostics; my @array = (3, 4, 1, 4, 7, 7, 4, 1, 3, 8); my %unordered; @unordered{@array} = undef; # array context takes values of array in u +nordered array which is a hash so keys cannot be same and their value +s are undef; foreach my $key (keys %unordered) { print "Unordered: $key \n"; } my %seen; my @ordered; foreach my $element (@array) { if( not $seen{$element}++ ) { push @ordered, $element; } } foreach my $element (@ordered) { print "Ordered: $element \n"; }

Replies are listed 'Best First'.
Re: Understanding Perl context
by Athanasius (Archbishop) on Apr 19, 2015 at 03:11 UTC

    Hello shankonit, and welcome to the Monastery!

    The statement my %seen creates an empty hash. Then the foreach loop works as follows: On the first iteration, $element is 3 (the first element of the array @array). The if statement looks up the hash value $seen{$element}, i.e. $seen{3}, but of course doesn’t find it because the hash is empty. But the code says to increment (++) that element, so an important feature of Perl called autovivification comes into play: the hash element $seen{3} is created, with an initial value of undef, which is silently interpreted as 0 (zero) and incremented to 1. But because the postincrement form is used (i.e., $x++ instead of ++$x), the value of the expression $seen{$element}++ is the value it had before the increment, which in this case is undef. And since undef is false, negating this false value with not makes it true, so the if clause is entered and $element is pushed onto the array @ordered.

    Likewise for the subsquent iterations in which $element is equal to 4 and then to 1. But when $element is set to 4 a second time, the expression $seen{$element} is already present in the hash, and has the value 1, which is true. Negating it makes it false, so the if clause is not entered. The result is that each element of @array is added only once to the new array @ordered.

    The point of this is presumably to show how to convert an array with duplicates: @array = (3, 4, 1, 4, 7, 7, 4, 1, 3, 8); into an array with the same elements but all duplicates removed: @ordered = (3, 4, 1, 7, 8). Unfortunately, the new array is misnamed: it isn’t “ordered,” it is rather an array of unique values. The code has nothing to do with ordering, only with the removal of duplicate elements.

    Update 1: On autovivification, see the references in this node. On removing duplicates from an array, see How-can-I-remove-duplicate-elements-from-a-list-or-array of perlfaq4.

    Update 2: On second thoughts, I think the hash %unordered and array @ordered have been so-named to highlight an important difference between the first technique and the second: namely, that the second preserves the order of the first occurrences of each element in the original array, whereas the first technique does not (because hashes, unlike arrays, are unordered).

    Hope that helps,

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

      thnx...it helped a lot.

Re: Understanding Perl context (removing duplicates from array)
by LanX (Saint) on Apr 19, 2015 at 07:06 UTC
    The code shows two approaches to remove duplicates in @array .

    The first @unordered approach is using a hash slice, and since hash keys are unique, duplicates are overwritten. But the original order is lost, since keys returns in random order.

    The second @ordered approach rejects duplicates from copying, hence @ordered preserves the order of the first occurrences. This is done with %seen which postincrements the value per element

    if(  not $seen{$element}++ )

    Only 0 is false, hence repeated (i.e. "unseen") elements are filtered out.

    PS: As a side note:

    A hash slice initializing undef values is better written as @unordered{@array}=() , assigning undef to the first element is misleading!

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      A hash slice initializing undef values is better written as @unordered{@array}=() , assigning undef to the first element is misleading! [emphasis added]

      IMHO, this statement is itself a bit misleading. The two forms of the statement are exactly equivalent! The effect of either  () or  undef as the RHS of the assignment is exactly the same because both are equivalent to lists of  undef values of exactly the same length as the  @array array. This slightly obscure point must be understood regardless of the assignment used, and neither assignment seems to me better suited to making this point.


      Give a man a fish:  <%-(-(-(-<

        Its misleading, because the next maintainer might think he can replace undef with 0 and bang!

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

Re: Understanding Perl context
by AnomalousMonk (Archbishop) on Apr 19, 2015 at 13:25 UTC

    Just a side note: The OP does not really seem to be about "context" in Perl, which is a very particular idea. Please see Context tutorial for a discussion.


    Give a man a fish:  <%-(-(-(-<