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

Hi, can any one tell me what (how) the following code actually does: @o{qw(a b c)} = ($a, $b, $c); "o" looks like it wants to be an array, but the only thing that seems to print values is if you treat "o" like a hash. like so: foreach $x (keys %o) { print "$x, $a{$x}\n"; } what gives? thanks

Replies are listed 'Best First'.
Re: beyond me (It's a hash slice)
by dragonchild (Archbishop) on Jul 25, 2001 at 21:56 UTC
    What that's doing is known as a hash slice. It's a shorthand for:

    $o{'a'} = $a; $o{'b'} = $b; $o{'c'} = $c;
    <GUESS>The reason for the @ symbol is because assigning values from one ordered list to another. (Hashes are unordered, so you have to order them, especially if you want this to be an rvalue.)</GUESS>
      The type of brackets ({} here) after the variable name indicate the type of of the variable. The symbol in front of the variable name just tells you the return type of the expression.

      Uri Guttman explains hash slices here.

        Excellent link, PrakashK, to Uri Guttman article about hash slices! Now I got it!
        I was told that one is not using perl if not using hashes, and I was trying. Now I was enlighted (again): hash slices are really cool! I have couple ways how to use them. Must-read article!

        pmas
        To make errors is human. But to make million errors per second, you need a computer.

How I learned to stop worrying and love the hash slice. (boo)
by boo_radley (Parson) on Jul 25, 2001 at 22:18 UTC
    this is called a hash slice.
    someone has declared o as a hash, and wants to set up 3 elements in semi-odd way.
    it's equivalent to
    %o= (a=>'a', b=>'b', c=>'c');
    except that you can't declare a hash under strict this way -- you still need to explicity code my %o, and then use the hash slice.

    So what's it good for?
    Primarily when you're populating an array from a hash, and you only want to use a subset of the hash's values. It can be onerous to type @ary=($hash{e}, $hash{f}, ... $hash{n}), if you only wanted those particular elements, so the almight Wall-Oz gave us the hash slice, which reduces the assignment to @ary=@hash{"e".."n"} (yes, there are other ways of doing this, I acknowledge)
    Here's a small example for you showing the hash slice in action :

    use strict; my %o; @o{qw(a b c d e f)} = (1,2,3,4,5); my @foo; @foo=@o{"c".."e"}; print @foo;
    the third line uses a hash slice to assign 5 values to %o. the fifth line uses a hash slice to easily take 3 elements from %0, and assign their values into @foo.
    Also, even though I haven't demonstrated it, using
    @o("a","e","f")
    is just as legal.

    See also array slices for a similar concept, but with arrays.

    update : hey, neat! @foo=@bar{sort keys %bar};

      So, why can't you intialize a slice under 'use strict'? In fact, even under 'no strict', you can't initialize a slice with 'my' nor 'our'. Same thing with array slices.

      I ran into this restriction recently, and was just curious as to the reason.

      -jehuni

        Probably because "my @foo{qw(a b c)} = (1 .. 3)" indicates to the compiler that you're trying to create a list, but it then sees the {} and gets confuzzled.

        Update: If you don't use strict and you don't use my or our, the compiler is fine. :)

      I frequently use hash slices to eliminate duplicates from an array.

      A simplified example:

      my @a = (1,2,3,2,1); my %a; @a{@a} = (1) x @a; my @unique_a = keys %a;
      Of course, this does not preserve the order of the elements as they were in original array @a.
        Perhaps grep might work better here?
        my @a = (1,2,3,2,1); my %seen; my @unique_a = grep {!$seen{$_}++} @a; print "A: $_\n" for (@a); print "Unique: $_\n" for (@unique_a);
        Assuming you wanted to keep the first occurance, this will preserve the original order.

        Or you can use map to create your %a hash.

        my @a = (1,2,3,2,1); my %a = map {$_=>1} @a; my @unique_a = keys %a; print "A: $_\n" for (@a); print "Unique: $_\n" for (@unique_a);

        -Blake