6

I have an array, @array, of array references. If I use the range operator to print elements 1 through 3 of @array, print @array[1..3], perl prints the array references for elements 1 through 3.

Why when I try to dereference the array references indexed between 1 and 3, @{@array[1..3]}, perl only dereferences and prints out the last element indexed in the range operator?

Is there a way to use the range operator while dereferencing an array?

Example Code

#!/bin/perl

use strict;
use warnings;

my @array = ();
foreach my $i (0..10) {
    push @array, [rand(1000), int(rand(3))];
}

foreach my $i (@array) {
    print "@$i\n";
}

print "\n\n================\n\n";

print @{@array[1..3]};

print "\n\n================\n\n";
EarthIsHome
  • 655
  • 6
  • 18

4 Answers4

4

From perldata:

Slices in scalar context return the last item of the slice.

@{ ... } dereferences a scalar value as an array, this implies that the value being dereferenced is in scalar context. From the perldata quote above we know that this will return the last element. Therefore the result is the last element.

A more reasonable approach would be to loop through your slice and print each individual array reference:

use strict; 
use warnings; 

use feature qw(say);

my @array_of_arrayrefs = (
   [qw(1 2 3)],
   [qw(4 5 6)],
   [qw(7 8 9)],
   [qw(a b c)],
);

foreach my $aref ( @array_of_arrayrefs[1..3] ) {
   say join ',', @$aref;
}
Hunter McMillen
  • 59,865
  • 24
  • 119
  • 170
3

@{@array[1..3]} is a strange-looking construct. @{ ... } is the array dereference operator. It needs a reference, which is a type of scalar. But @array[ ... ] produces a list.

This is one of those situations where you need to remember the rule for list evaluation in scalar context. The rule is that there is no general rule. Each list-producing operator does its own thing. In this case, apparently the array slice operator used in scalar context returns the last element of the list. @array[1..3] in scalar context is the same as $array[3].

As you have noticed, this is not useful. Array slices aren't meant to be used in scalar context

If you want to flatten a 2-dimensional nested array structure into a 1-dimensional list, use map:

print join ' ', map { @$_ } @array[1..3]

You still use the range operator for slicing. You just need some kind of looping construct (e.g. map) to apply the array dereference operator separately to each element of the outer array.

  • 4
    ***“The rule is that there is no general rule”*** That just isn't true. A *list* in scalar context evaluates to the last element of the list. An *array* in scalar context evaluates to the number of elements in the array. – Borodin Jul 22 '15 at 17:57
  • 2
    @Borodin, Re <<“The rule is that there is no general rule” That just isn't true.>> So what's the general rule? You only mentioned two specific operators. What general rules covers the 100s of others? What about the one the OP used (array slice)? The thing is, Wumpus is correct. [**There isn't a general rule**](http://www.perlmonks.org/?node_id=347416). – ikegami Jul 22 '15 at 18:06
  • @ikegami: Oh dear. Announcing that there is no pattern, rhyme or reason to Perl's behaviour is just fodder to its detractors and isn't true. I didn't mention *any* operators, but the binding operator `=~` and the horrible smart match `~~` are irregular. Everything else is more or less predictable, which is what ***general*** means. And I'm going to bail out right now before you indulge yourself in another unprovable semantics argument. – Borodin Jul 22 '15 at 18:35
  • 1
    I was quoting (approximately, from memory) perlfunc itself. Here's the exact sentence: `There is no rule that relates the behavior of an expression in list context to its behavior in scalar context, or vice versa.` –  Jul 22 '15 at 18:39
  • 1
    @Borodin, Re "I didn't mention any operators", You gave the behaviour of the list operator and the array operator while incorrectly claiming there's a general rule governing what operators return in scalar context. **It's impossible to predict how an operator will behave in scalar context; there is no rule or guidelines to help.** – ikegami Jul 22 '15 at 18:45
  • And yet millions of Perl programmers daily write fully-functional software without memorising huge tables of context and behaviour per operator and function. Amazing! – Borodin Jul 22 '15 at 18:49
  • 1
    @Borodin, Yup, but they did it by learning or looking up how those operators behave in scalar context, not by learning some non-existent rule. (Aside from the obvious one: If an operator normally returns a scalar in list context, it'll return that same scalar in scalar context) – ikegami Jul 22 '15 at 18:50
  • Is there documentation explaining that `@{ }` evaluates its operand in scalar context? (It makes sense conceptually, but I'm just curious.) – ThisSuitIsBlackNot Jul 22 '15 at 18:59
  • @ThisSuitIsBlackNot, That's really just an array, so it should and does behave as `@a`. In fact, if `@a` isn't a lexical, there's no difference between `@a` and `@{"a"}` except when the glob is located. – ikegami Jul 22 '15 at 19:51
  • @ikegami I didn't know you could do `@{"a"}`, thanks. After reading [this](http://stackoverflow.com/a/2338348/176646), I *think* I understand how `@{@array[1..3]}` is parsed (`@array[1..3]` is not an identifier so it's evaluated as code; if the result is a reference, it's dereferenced, if not, the result is stringified and treated as a symbolic reference). What I don't get is where scalar context comes into play; `print` imposes list context on its operands, so I would expect the slice in `print @{@array[1..3]}` to be evaluated in list context. Can you explain? – ThisSuitIsBlackNot Jul 22 '15 at 22:41
  • @ThisSuitIsBlackNot, It's called a "symbolic reference", and it's what `use strict 'refs';` prevents. `@{ ... }` needs a reference (a scalar), so it evaluates the block in scalar context, so `@array[1..3]` returns `$array[3]`. (Probably warns too.) Whatever's inside is treated as a real or symbolic reference. – ikegami Jul 22 '15 at 23:04
  • @ikegami Thanks. I think I found where this happens...`Perl_newAVREF` in op.c ends with `return newUNOP(OP_RV2AV, 0, scalar(o));` – ThisSuitIsBlackNot Jul 24 '15 at 14:47
  • @ThisSuitIsBlackNot, That happens regardless of context. In fact, I think context is applied later. The sub you want is probably named `pp_rv2av` – ikegami Jul 24 '15 at 15:13
  • @ThisSuitIsBlackNot, Look at the `if (is_pp_rv2av) {` bit in `pp_rv2av`. `gimme` is the context. `G_ARRAY` indicates list context. You can guess `G_SCALAR` and `G_VOID`. The code literally does `return wantarray ? @av : defined(wantarray) ? $#av + 1 : ();` – ikegami Jul 24 '15 at 15:51
1

The @{ ... } construction dereferences the scalar value of the code within the braces as an array

I'm unclear what you expect from @{ @array[1..3] }, but the list@array[1..3] in scalar context returns just the last element of the list -- $array[3] -- so you are asking for @{ $array[3] } which I guess is what you got

If you explain what you want to print then I am sure we can help, but dereferencing a list makes little sense

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 2
    `@array[1..3]` isn't a list; it's an array slice. In list context, it returns the last scalar that would have been returned in list context. This is actually different than a list in scalar context. – ikegami Jul 22 '15 at 18:11
  • ***"In list context, it returns the last scalar that would have been returned in list context"*** Uh, it *is* in list context. Do you want to rewrite that? – Borodin Jul 22 '15 at 18:39
  • 1
    s/In list context/In scalar context/ – ikegami Jul 22 '15 at 18:47
1

@array[1..3] is a list of 3 array references. You can't dereference them all at once, so you should iterate over this list and dereference each element separately:

print @$_ for @array[1..3];
print "@$_\n" for @array[1..3];  # for better looking output
mob
  • 117,087
  • 18
  • 149
  • 283