http://www.perlmonks.org?node_id=866291

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

I have always understood that perl is context driven, and I'm quite happy that the "equals" assignment gets over-loaded depending on context:
@a = qw(a b c d e); @b = @a; # @b contains (a b c d e) $b = @a; # $b is 5, as the "=" is overloaded by context
I was teaching an "Introduction to Perl" course the other day, and one of the students had this code (I've paraphrased, as it were):
@a = qw(a b c d e); $b = @a[1,3]; print $b

Now, My understanding is that the code would print "2", being the number of elements in the array-slice. it prints "d"

Indeed, my slides make the point that @array[$n] is a one-element array, not a scalar, thus $b=@array[$n] will set $b to 1, not the value of the array element ($b=$array[$n])!
@a = qw(a b c d e); $b = @a[1]; print $b
prints "b"
@a = qw(a b c d e); @b = @a[1]; $b = @b print $b
prints 1

WTF?

So, I tried forcing a scalar context on the assignement:

@a = qw(a b c d e); $b = scalar @a[1,3]; print $b

.... still prints "d"!

Switch the array-slice around, and we still get the last element in the scalar:
@a = qw(a b c d e); $b = @a[3,1]; print $b

prints "b"

Can someone explain why?


-- Ian Stuart
A man depriving some poor village, somewhere, of a first-class idiot.

Replies are listed 'Best First'.
Re: Confused as to why the "casting context" is mis-behaving
by philipbailey (Curate) on Oct 20, 2010 at 11:47 UTC

    This is one of those times when there is a difference between a list and an array in Perl. It seems hard to produce a concise definition of the difference, but to a first approximation an array is a list with a name (such as @a in your examples). So qw(a b c d e) is a list which you have assigned to @a.

    What you have exposed is that in scalar context, a array and list (both containing the same data) have different values. The value, in scalar context, of an array, is the number of elements. The value, in scalar context, of a list, is the last item in that list.

    Update. See perlfaq4

      Hmm. Please point to documentation. Your description of what's happening jibes with what we see, but I had the same impression as the original poster, i.e. that this was not the way it would work.

      If there is no documentation, and I couldn't find any, then there needs to be. I'm not sure where to put it though. I looked in perlop under comma and perldata under slices.

      In fact, under perltrap, I found:

      ? Comma operator in scalar context gives scalar context to args The comma operator in a scalar context is now guaranteed to give scalar context to its arguments. @y= ('a','b','c'); $x = (1, 2, @y); print "x = $x\n"; # Perl4 prints: x = c # Thinks list context interpolates list # Perl5 prints: x = 3 # Knows scalar uses length of list
      Which I would take to be documentation to the contrary.

      I'm confused.

        from perlop:

        Comma Operator
        Binary "," is the comma operator. In scalar context it evaluates its left argument, throws that value away, then evaluates its right argument and returns that value. This is just like C’s comma operator.

        In list context, it’s just the list argument separator, and inserts both its arguments into the list. These arguments are also evaluated from left to right.

        This is documented in perldata, though maybe not as clearly or concisely as one might hope.

        The first issue is whether a slice is a list or not, ruling out the possibility that it is an array.

        This isn't stated clearly, as one might hope, in Slices. In that section it is, at best, only hinted at by statements like:

        A slice accesses several elements of a list, an array, or a hash simultaneously using a list of subscripts. It’s more convenient than writing out the individual elements as a list of separate scalar values.

        and

        Since you can assign to a list of variables, you can also assign to an array or hash slice.

        Perhaps the closest this section comes is:

        Since you can assign to a list of variables, you can also assig +n to an array or hash slice. @days[3..5] = qw/Wed Thu Fri/; @colors{'red','blue','green'} = (0xff0000, 0x0000ff, 0x00ff00); @folks[0, -1] = @folks[-1, 0]; The previous assignments are exactly equivalent to ($days[3], $days[4], $days[5]) = qw/Wed Thu Fri/; ($colors{'red'}, $colors{'blue'}, $colors{'green'}) = (0xff0000, 0x0000ff, 0x00ff00); ($folks[0], $folks[-1]) = ($folks[-1], $folks[0]);

        But this only states the exact equivalence in the case of assignment to a slice. It doesn't state that there is an exact equivalence in all cases.

        There are other indications. In Context one finds (emphasis mine):

        or slice, which is just a list anyway)

        And in Variable names one finds:

        @days[3,4,5] # same as ($days[3],$days[4],$days[5]) @days{'a','c'} # same as ($days{'a'},$days{'c'})

        So, perldata does not clearly, concisely and definitively state that slices and lists are equivalent in the obvious place (Slices), but none the less,if one reads carefully, I think this is the conclusion one would come to.

        If one accepts that a slice is equivalent to a list, then this leaves the question of what a list evaluates to in scalar context.

        In Scalar values one finds (emphasis mine):

        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.)

        The Note about lists is equivalent to the statement: If you evaluate a list in scalar context, it returns the last value, like the C comma operator.

        Thus we have the two relevant facts documented, more or less:

        • A slice is a list
        • A list, evaluated in scalar context, returns the last value

        These together predict the observed behavior.

        The quote from perltrap is interesting but I don't find it inconsistent with any of the above. It really should be covered under Context. They take care to state the context of the righthand side in the case of assignment to a list, but don't cover this less obvious case.

Re: Confused as to why the "casting context" is mis-behaving
by tinita (Parson) on Oct 20, 2010 at 12:19 UTC
    @a[3,1] is an array slice, and an array slice is effectively the same as ($a[3], $a[1]), which is the comma operator.
    and if the comma operator is called in scalar context it returns the last expression, $a[1].
Re: Confused as to why the "casting context" is mis-behaving
by AnomalousMonk (Archbishop) on Oct 20, 2010 at 12:27 UTC
    ...  @array[$n] is a one-element array...

    No. As philipbailey has pointed out, it is a (single element) list, and a list is not an array, and an array is not a list! ikegami has many postings on the differences between the two, but I can't come up with a good one at the moment; try Super Search.

    Update: For another difference between arrays and lists:

    >perl -wMstrict -le "my @ra = qw(a b c d); print qq{@ra}; for my $elem (@ra) { print ++$elem; } print qq{@ra}; ;; for my $elem (qw(a b c d)) { print ++$elem; } " a b c d b c d e b c d e Modification of a read-only value attempted at -e line 1.
Re: Confused as to why the "casting context" is mis-behaving ("list vs array", again)
by tye (Sage) on Oct 21, 2010 at 05:24 UTC

    Ah, lots of constructing of the false dichotomy of "list" vs "array". "It returns/is a list and therefore returns its last item in scalar context" is utter nonsense, though quite persistent nonsense in part because it gives the "right answer" in several cases. Also because "list" is very often used to mean "list literal" (use of the comma operator) and is also very often used to talk about what is returned by lots of different things, including slices.

    So, yes, a list literal gives its last item in scalar context (be careful how you define "item", though). Yes, a slice returns a list of scalar values. Yes, lots of things that return a list of scalar values also return the last item when in scalar context. Yes, slices do that.

    But, no, a list of scalar values is not a list literal (a list literal does return a list of scalar values when used in a list context). No, a slice does not return a list literal; a list literal is a syntax construct and thus is not something that can be "returned". No, not all things that return a list of scalar values give the last item when in scalar context. No, a slice is not just syntactic sugar for a list literal.

    And, no, the last time I checked, what a slice returns in scalar context is not even documented. Nor was it designed. It came to be as a quick patch to fix a core dump and no documentation change accompanied it.

    Yes, grep and map both return lists of scalar values. Yes, they both return the number of elements when in a scalar context. By the logic of the "is it a list or an array?" meme, clearly grep and map must return arrays. No, they don't return arrays (for example, you can push to an array). You can't even return an array in Perl (you can return a reference to an array and you can write "return @array;" but that returns (a copy of) the list of scalar values that are in the array).

    The reality is that each operation type in Perl that can return a list (of scalar values) also has a specific thing it will return when in a scalar context. Almost all of those cases are documented. The majority of cases return the last item. Several return a count. Several return the first item. Even more do something else.

    Slices return the last item because that is the easiest thing to do in a quick patch (you just reduce the stack size to 1) and that quick patch got accepted. Period.

    (Update) On Scalar Context lists many of the cases.

    - tye        

      And, no, the last time I checked, what a slice returns in scalar context is not even documented.

      now changed in blead perl

      Perl 6 - links to (nearly) everything that is Perl 6.
        Patch is wrong. $u is assigned '2'

        ...which appears to have been fixed already. It would have been clearer had the data been adjusted instead of the comment.

      The dichotomy is false, for the reasons that you give, but I would argue that is by no means "nonsense". As a simplification, a model, for the behaviour of Perl, the list vs. array distinction remains useful. It is better to have a model, with exceptions, than no model at all.

        The reason I point out the nonsense of it is because I repeatedly see people make mistakes based on the model. I don't think having an incorrect model that leads to the right answer for the wrong reasons in a few cases but also leads to a fundamental misunderstanding and also leads to the wrong answer in a lot of other cases is better than having a better model.

        The alternative is not "no model at all". The alternative is actually a simple model that matches reality much better.

        - tye        

      I think the absence of documentation you note is the root of the persistence of the nonsense. In the absence of information in common references, many people are inclined to make up answers to their questions based on the little they know.

      I wonder what your thoughts are about the prospect of getting perldata, perlfunc, perlsyn and perlop updated to provide less nonsensical descriptions of what the data structures, operators, functions and behaviors actually are, to cover the current omissions, so that people are less inclined to make up nonsense.

        patches are always welcome :)
      You can't even return an array in Perl (you can return a reference to an array and you can write "return @array;" but that returns (a copy of) the list of scalar values that are in the array).

      This is not 100% correct. The array is returned from the function. But to get a hold of it, you should COPY it somewhere. Either in another array, or into list of variables. Or where ever.

      perl -e 'sub test{my @a=qw(a b c); return @a} my $v = test(); print $v +,"\n"' perl -e 'sub test{return qw(a b c);} my $v = test(); print $v,"\n"' perl -e 'sub test{my @a=qw(a b c); return @a} my ($a, $v) = test(); pr +int $v,"\n"' perl -e 'sub test{my @a=qw(a b c); return (@a)} my $v = test(); print +$v,"\n"' perl -e 'sub test{my @a=qw(a b c); return (@a, "b")} my $v = test(); p +rint $v,"\n"'

      Everything in perl becomes logical if you think about @abc as about OBJECT, and about list as COLLECTION of objects. So obtaining a slice from the array gives you collection of objects. grep or map work with collections of objects, so if you pass array to them, this array is turned into collection of objects. Of course, neither grep nor map can return array since they have only collection of objects as input.

      Placing array as member into collection/list does not make this array disappear. It stays in the list as the object. But when the list is copied somewhere, then the array object might be replaced with its elements. Depends on the context.

        This is not 100% correct. The array is returned from the function. But to get a hold of it, you should COPY it somewhere.

        No, the array is copied before the function returns. You can observe that:

        my @a= ( 'a' .. 'e' ); sub returnArray { return @a; } my @b= ( 'v' .. 'z' ); for( returnArray() ) { $_ .= '?' } for( @b ) { $_ .= '!' } print "@a\n"; print "@b\n"; __END__ a b c d e v! w! x! y! z!

        And it is also not true that the function returns and then (after that) the scalar context decides to get the size of the array while the list context decides to copy the elements of the array.

        The context gets passed in to the function, to the returning statement so that it can, when possible, avoid the work of creating a whole list of values just to throw that list away.

        The method of returning information from a Perl function is to push zero or more scalar values onto the stack. return @array;, if called in a list context, loops over the array and pushes onto the stack, a copy of each element of the array. If you assign the return value to an array, then that assignment operator loops over the items on the stack and puts them into the array (whether each value is copied again here or the array just gets aliases to each of the original copies is an implementation detail and a matter of optimization that I have never needed to worry about).

        - tye        

Re: Confused as to why the "casting context" is mis-behaving
by TomDLux (Vicar) on Oct 20, 2010 at 15:21 UTC

    Interesting and surprising.

    DB<1> @a = qw( a b c d e ) DB<2> x @a[1,3] 0 'b' 1 'd' DB<3> x scalar @a[1,3] 0 'd' DB<4>

    In my mind, the 'scalar' applies to the result of the slice, but obviously it 'reaches into' the subscripting square brackets to create a slice of the rightmost subscript.

    As Occam said: Entia non sunt multiplicanda praeter necessitatem.

      not surprising because an array slice is basically just shorter for:
      ($a[1], $a[3])
      and
      scalar ($a[1], $a[3])
      will return $a[3].
      In my mind, the 'scalar' applies to the result of the slice, but obviously it 'reaches into' the subscripting square brackets to create a slice of the rightmost subscript.

      That is what I wished happened. If I'd bothered to try to design what it should do rather than throw together the quickest patch possible, I would have made it do that (at least the me of today would have).

      But, no, that isn't actually what happens. You can see that easily enough:

      my @a= ('a'..'d'); my @b= (4,2,1); print scalar @a[0,@b], $/; print @a[scalar(0,@b)], $/; __END__ b # $a[1] because $b[-1] is 1 d # $a[3] because @b has 3 elements

      - tye        

Re: Confused as to why the "casting context" is mis-behaving
by Anonymous Monk on Oct 20, 2010 at 14:07 UTC