views:

175

answers:

2

Ok, I understand perl hash slices, and the "x" operator in Perl, but can someone explain the following code example from here (slightly simplified)?

sub test{
    my %hash;
    @hash{@_} = (undef) x @_;
}

Example Call to sub:

test('one', 'two', 'three');

This line is what throws me:

@hash{@_} = (undef) x @_;

It is creating a hash where the keys are the parameters to the sub and initializing to undef, so:

%hash:

'one' => undef, 'two' => undef, 'three' => undef

The rvalue of the x operator should be a number; how is it that @_ is interpreted as the length of the sub's parameter array? I would expect you'd at least have to do this:

@hash{@_} = (undef) x scalar @_;
A: 

In scalar context, an array evaluates to its length. From perldoc perldata:

If you evaluate an array in scalar context, it returns the length of the array. (Note that this is not true of lists, which return the last value, like the C comma operator, nor of built-in functions, which return whatever they feel like returning.)

Although I cannot find more information on it currently, it seems that the replication operator evaluates its second argument in scalar context, causing the array to evaluate to its length.

MvanGeest
The RHS of the `x` operator has to be a number (or something that looks like a number) so it does indeed evaluate it in scalar context.
friedo
it just confuses me as you usually use the $ sigil for scalar and the @ sigil for an array, yet this example just uses the @ sigil... so it's just implicit you're after the scalar? with a standard array @arr you'd get length by the scalar context $arr. Maybe the question is why is it done this way - because $_ is a special variable?
No, that has nothing to do with it - try any other array and it will work. As friedo commented, the 'scalarization', i.e. the scalar context, is caused by the replication operator. If you want to be explicitly clear, you can use `scalar @array` or `scalar(@array)` so you won't forget you're looking at a scalar.
MvanGeest
@user210757 `$foo` is a *completely unrelated variable* to `@foo`.
hobbs
hobbs - my mistake, I was thinking "@foo" vs "scalar @foo" - the scalar context of foo.
operands that are expected to be scalars get scalar context (though there are some operators that you might expect would expect a scalar that don't: unary + and \ (and maybe some other(s) I've forgotten))
ysth
+9  A: 

To figure out this code you need to understand three things:

The repetition operator. The x operator is the repetition operator. In list context, if the operator's left-hand argument is enclosed in parentheses, it will repeat the items in a list:

my @x = ('foo') x 3;  # ('foo', 'foo', 'foo')

Arrays in scalar context. When an array is used in scalar context, it returns its size. The x operator imposes scalar context on its right-hand argument.

my @y = (7,8,9);
my $n = 10 * @y; # $n is 30

Hash slices. The hash slice syntax provides a way to access multiple hash items at once. A hash slice can retrieve hash values, or it can be assigned to. In the case at hand, we are assigning to a hash slice.

# Right side creates a list of repeated undef values -- the size of @_.
# We assign that list to a set of hash keys -- also provided by @_.
@hash{@_} = (undef) x @_;

Less obscure ways to do the same thing:

@hash{@_} = ();
$hash{$_} = undef for @_;
FM
+1 for `@hash{@_} = ();`, that's the idiomatic way.
rjh
very clear explanation. it seems you just have to know that the repetition x operator is going to user your right hand arg internally in a scalar context even if it's an array.
it's a list repeat when in list context *AND* the left operand is enclosed in parentheses (or is a qw). And, not or.
ysth